【问题标题】:Find all files inside a folder that do not contain a whitespace查找文件夹中不包含空格的所有文件
【发布时间】:2015-08-04 15:45:59
【问题描述】:

我正在尝试编写一个 bash 脚本来查找目录中不包含空格的所有文件,但我发现了一些困难。我正在使用 find 和 grep 的组合,它似乎适用于除空白之外的所有内容。我都试过了:

find $d -name '*.js' | xargs grep -L '[ \t]*'

find $d -name '*.js' | xargs grep -L '[[:space:]]'

两者都不起作用。如何正确匹配空格?

编辑:澄清一下,我想在文件中搜索,而不是在文件名中。

【问题讨论】:

  • 您可能想要[ \t]+ 作为第一个; 每个文件将包含空字符串,[ \t]* 匹配。
  • 这些怎么不起作用?
  • 您是在寻找不包含空格的文件,还是文件名称
  • 我想在文件中搜索
  • @GianLucaScoccia 感谢您的澄清。我已经进行了一些修改,以便现在更好地回答。

标签: bash grep


【解决方案1】:
find . -type f \( -exec grep -q '[[:space:]]' {} \; -o -print \)

当 grep 找到带有空格的文件时,它返回“成功”。如果-exec中的命令成功,则find允许下一个谓词操作;但是,如果下一个运算符是“OR”的-o,那么find 仅在-exec 中的命令成功时才允许下一个谓词进行操作。这就是上述工作的原因:它匹配具有空格的文件,但只打印出名称与 不匹配 的文件。 (括号是必需的,因此 -type f 也不受“或”的约束——否则我们会得到所有不是文件的东西,比如目录名。)你可以限制它只是到 *.js 文件,如果你喜欢:

find . -type f -name '*.js' \
    \( -exec grep -q '[[:space:]]' {} \; -o -print \)

值得注意的是,如果您想检测换行符grep 并不是一个好工具。为此,您可能会考虑一些蛮力:

for file in "$d"/*.js; do
    origcheck=$(md5sum < "$file")
    nospacecheck=$(tr -d '[:space:]' < "$file" | md5sum)
    [[ "$origcheck" = "$nospacecheck" ]] || printf '%s\n' "$file"
done

这将创建每个匹配文件的校验和,其中包含和不包含所有空格。如果校验和相同,则文件永远不会有任何空格。 (但许多文件以换行符结尾,所以要小心。)

原始方法的注意事项:

我电脑上的 grep 手册页说

-L … Only the names of files not containing selected lines are written…
     If the standard input is searched, the string ``(standard input)'' is written.

但是the standards 没有提到-L,因此不能保证它在其他实现中的行为方式。以下是一些实验:

快速健全性检查:

$ grep -L '[a]' <<< 'a'
$ grep -L '[a]' <<< 'b'
(standard input)

到目前为止,一切都很好。

$ grep -L '[ \t]' <<< 'ab c'
$ grep -L '[ \t]' <<< $'ab\tc'
(standard input)

(在 bash 中,我们可以用一种特殊形式的引用来解释反斜杠转义,来编写制表符和换行符之类的文字字符。这里,$'\t' 扩展为文字制表符。)所以我们看到带有空格的字符串是匹配,但带有文字制表符的字符串不匹配。

$ grep -L '[ \t]' <<< t
$ grep -L '[ \t]' <<< '\'
$

文字't'是匹配的事实证明反斜杠-t不是grep的制表符。文字反斜杠也是匹配项,因此 grep 似乎以面值采用该表达式。好吧,我们知道一种表达真实标签的方法:

$ grep -L $'[ \t]' <<< $'\t'
$ grep -L $'[ \t]' <<< 't'
(standard input)
$ grep -L $'[ \t]' <<< '\'
(standard input)

所以原始表达式的问题在于我们不是在寻找没有空格或制表符的文件:我们在寻找没有空格、反斜杠或“t”字符的文件。

直到现在我都避免谈论*,但它匹配零个或多个字符,所以即使你让字符类匹配正确的字符,在它后面加上星号也不会得到你想要的结果:

$ grep -L $'[ \t]*' <<< $'\t'
$ grep -L $'[ \t]*' <<< t
$

上述输入字符串是否包含零个或多个制表符?是的。他们两个都这样做。你只想找到一个字符,所以不要复杂。

但是[[:space:]] 呢?

$ grep -L '[[:space:]]' <<< ' '
$ grep -L '[[:space:]]' <<< $'\t'
$ grep -L '[[:space:]]' <<< x
(standard input)

嗯,这个我无法解释。在我测试过的两台机器(OS X 和 Linux)上,这一切都按预期工作。也许您最初在 '[[:space:]]' 之后有一个星号?我不知道。这是个谜。

find … | xargs

find 连接到 xargs 本身可能会带来问题。 shell 对参数名称进行分词,这样的管道可能会丢失有关正在传递的实际文件名中的空格的信息。这是一个非常罕见的案例,许多人根本不去想或不在乎它,但它可以而且确实发生了,而且解决起来并不难。

首先,find-exec,所以不是

find . -some -predicate | xargs some command

你可以简单地写

find . -some -predicate -exec some command {} +

如果出于某种原因,您真的想使用 xargs(也许您想利用并行化),那么告诉 find 和 xargs 文件名要用 NUL 字符而不是空格分隔:

find . -some -predicate -print0 | xargs -0 some command

【讨论】:

  • 好消息是它有效,我将把它标记为已接受的答案,坏消息是(以我有限的 bash 技能)我完全不知道 如何 它有效。需要解释一下吗?
  • 我认为 OP 的原始解决方案没有任何改进,也没有试图解释它如何可能不起作用。
  • @tripleee 好电话。已更新。
【解决方案2】:

您可以使用grep 简单地排除包含find 结果中的空格的结果(而不是-v, --invert-match select non-matching lines)...类似于:

查找不带空格的文件名的解决方案

find $d -name '*.js' | grep -v " "

看起来效果不错!

根据您的编辑,下面的解决方案应该适合您,结果只是不包含空白内容的文件:

查找内容不包含空格的文件的解决方案,(在此解决方案中文件名可以包含空格。)

find $d -name '*.js' |grep -iRlv " "

如果您只想返回不包含空格的文件名以及那些没有空格的文件内容,我想这两者的组合可以工作:

find $d -name '*.js' |grep -iRlv " " | grep -v " "

【讨论】:

  • 这会查找不包含空格的文件名,而不是文件(这可能是 OP 想要的。)
  • 这也是模棱两可的,因为空格的存在(或不存在)可能在目录名中,而不是在基本名中,或者路径的一部分包含换行符。
  • 添加了查找文件的解决方案,即使文件名可能包含空格。
  • 另外,需要更多报价。 find "$d",而不是 find $d,或者带有空格或 glob 表达式的目录名称将表现不佳。
  • (另外,请参阅有关问题的 cmets:OP 已澄清他们关心文件内容中的空格,而不是文件名)。
猜你喜欢
  • 1970-01-01
  • 2020-11-15
  • 1970-01-01
  • 2012-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-14
  • 2013-12-25
相关资源
最近更新 更多