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