我有一个shell脚本
find . -name "*.java" -print0 | xargs -0 grep -Lz 'regular_expression'
以这种方式输出与正则表达式不匹配的文件名:
file1.java
file2.java
...
我理解的方式如下:find
查找所需文件并将其名称与\0
连接起来。然后xargs
将find
的输出与\0
分开并逐一将其提供给grep
。
然后我想再添加一个阶段并获得basename
个文件。我修改了脚本:
find . -name "*.java" -print0 | xargs -0 grep -LzZ 'regular_expression' | xargs -0 basename
但收到了错误。我开始调查并做了一个临时输出:
find . -name "*.java" -print0 | xargs -0 grep -LzZ 'regular_expression' | xargs -0 echo basename
得到了这个:
basename ./file1.java ./file2.java ./subdir/file1.java ./subdir/file2.java
因此,文件名未被\0
拆分。如果xargs
与grep
一起使用且未xargs
与basename
分开,我无法理解为什么会将其拆分。
我在后者-n1
中使用xargs
获得了解决方法。但我仍然不明白为什么我需要它(因为我没有在xargs
中使用grep
)以及此参数的用途。
希望你能向我解释一下-n1
做了什么以及为什么我在后一种用法中需要它,并且在grep
中不需要它。
答案 0 :(得分:3)
-n1
告诉xargs
每个参数运行给定的命令 。
所以,如果你有像
这样的东西echo file1 file2 file2 | xargs basename
这相当于
basename file1 file2 file2
但如果你这样做
echo file1 file2 file2 | xargs -n1 basename
这会导致xargs运行:
basename file1
basename file2
basename file2
至于xargs
的{{1}}标志,这是-0
选项的别名,它告诉--null
在xargs
上拆分而不是默认的空格。您需要在\0
之后使用它,因为find
的{{1}}带有\0
,但-print0
的结果是纯空格分隔的标记。
答案 1 :(得分:2)
文件名 由\0
分割。不同之处在于您使用的命令。 xargs
通常采用其标准输入,将其分解为列表(此处,通过拆分NUL),然后将该列表作为额外参数传递给您的命令。所以当你这样做时:
find . -name "*.java" -print0 | xargs -0 grep -Lz 'regular_expression'
实际运行的是:
grep -Lz 'regular_expression' file1.java file2.java file3.java...
在这里,-z
并不重要,因为它只影响grep
读取标准输入的方式,并且您不会向其标准输入发送任何内容。
因此,当您添加另一个运行xargs
的{{1}}时,您会得到:
basename
但是basename file1.java file2.java file3.java...
虽然会占用任意数量的文件名参数,但 grep
只需要一个而忽略其他参数。
basename
所在的位置:它告诉-n 1
将其参数列表分成块(1),并多次运行该命令。所以现在运行的是:
xargs
并且所有输出都连接在一起到stdout。