我有一个文件输入可能有空行,我想跳过任何空行。
我正在尝试找到一个不需要使用 grep / tr 并将输出通过管道传输到 xargs 的解决方案
我使用以下 xarg 选项
-r如果没有参数,我不希望运行该命令。 -d '\n'新行是分隔符。
使用回显命令:
echo "" | xargs -r -d'\n' -I {} echo "test '{}'"
输出:
test ''
使用由空行组成的文件的命令:
xargs -a /tmp/test.txt -r -d'\n' -I {} echo "test '{}'"
输出:
test ''
test ''
test ''
我可以如上所述使用 grep,但我想知道是否可以仅使用 xargs 来完成。
grep 示例(工作):
grep -v -e '^$' /tmp/test.txt | xargs -r -d'\n' -I {} echo "test '{}'"
答案1
-r
-I
不是忽略空元素,而是避免在输入没有元素时运行命令一次(尽管(也不是 BSD )不需要-J
)。
您通常-r
出于这个原因想要。
find . -criteria -print0 | xargs -0 ls -ld
如果find
没有找到任何东西,仍然会ls -d
在没有参数的情况下运行,并且会列出.
.
所以你确实想要:
find . -criteria -print0 | xargs -r0 ls -ld
如果什么也没找到ls
,则根本不运行find
。
请注意, 和-r
都是-0
GNU 扩展(同样,-d
可移植性更差)。-r
是某些 BSD 的默认行为,xargs
包括 FreeBSD 和 NetBSD(尽管这使得它们不符合 POSIX)。
(无论如何,xargs
在该特定示例中不需要像您一样find . -criteria -exec ls -ld {} +
)。
据我所知,GNUxargs
是唯一支持-d
选项的实现,并且它不支持过滤掉特定参数值,无论是空值还是其他值,因此使用grep
(grep -v '^$'
或LC_ALL=C grep .
) 是正确的方法。
如果您仍然想使用-a
(另一个 GNU 扩展),例如为了让命令的 stdin 保持不变,并且使用支持 ksh 样式进程替换(ksh、zsh、bash)的 shell,您可以这样做:
xargs -rd '\n' -a <(grep -v '^$' test.txt) cmd --
xargs -d '\n' -I{} -a <(grep -v '^$' test.txt) cmd -- {}
(rc、akanga、es、fish 和 yash shell 具有相同的功能,但语法不同)。
注意-0
是 like -d '\0'
,所以与 冲突-d '\n'
。您必须选择d
您想要的限制器。后者将优先,-0 -d '\n'
因为它排在最后。
-0
是首选分隔符,因为它是不能出现在命令参数或文件路径中的单字节值。-0
,与-d
现在在许多其他实现中发现的相反xargs
(包括大多数 BSD、Solaris、busybox、toybox、ast-open 的实现)。对于xargs
不支持的实现-d
(即除 GNU 之外的所有实现xargs
),您可以使用:
tr '\n' '\0' | xargs -0 ...
代替 GNU 的:
xargs -d '\n' ...
如果使用zsh
,而不是使用xargs
,您可以这样做:
for arg ( ${(f)"$(<test.txt)"} ) cmd -- $arg
参数f
扩展标志在换行符上拆分,并且由于${...}
未加引号,所以空元素将被删除。
和bash
:
readarray -t args < test.txt &&
for arg in "${args[@]}"; do
[ -z "$arg" ] || cmd -- "$arg"
done
您还可以通过不加引号的数组扩展来进行空删除,但是您还需要禁用通配符和分割,这也是在不加引号的参数扩展时完成的:
IFS=; set -o noglob
readarray -t args < test.txt &&
for arg in ${args[@]}; do
cmd -- "$arg"
done
您还可以执行以下操作:
while IFS= read <&3 -r arg || [ -n "$arg" ]; do
[ -z "$arg" ] || cmd -- "$arg" 3<&-
done 3< test.txt
这是标准sh
语法。
答案2
-0
我不想'
和其他角色逃走。
-r
我想忽略空白行。
-d '\n'
新行是分隔符。
-0
和的组合-d '\n'
对我来说似乎没有意义。-0
告诉xargs
它使用 NUL 字节作为分隔符,而-d '\n'
告诉它使用换行符作为分隔符。两者都禁用引号和反斜杠处理。
-0
,--null
输入项以空字符而不是空格终止,并且引号和反斜杠并不特殊(每个字符均按字面意思理解)。 [...]
--delimiter=delim
,-d delim
输入项以指定字符结束。 [...]处理输入时,引号和反斜杠并不特殊;输入中的每个字符均按字面意思处理。 [...]
根据我找到的 GNU 版本,如果两者都给出,则后一个似乎适用,比较例如:
$ printf 'foo\nbar\n' | xargs -0 -d'\n' -I {} echo "<{}>"
<foo>
<bar>
$ printf 'foo\nbar\n' | xargs -d'\n' -0 -I {} echo "<{}>"
<foo
bar
>
如果您的输入包含空元素,并且想要使用xargs
,我只需通过grep .
, 或grep -z .
NUL 分隔的值进行管道传输。 (或者grep -v '^$'
如果您需要接受仅包含字节序列的行不形成当前语言环境中的有效字符。)
无论如何,过滤行是有意义grep
的,并且由于您已经将其xargs
作为一个单独的进程进行分叉,因此添加过滤行grep
不会造成太大影响。此外,如果需要的话,也可以轻松修改正则表达式,例如删除仅包含空格的行。
答案3
回答我自己的问题以确认您必须使用 grep (或其他工具)并将输出通过管道传输到 xargs 来过滤掉空白输入。
解决方案:
grep -v '^$' /tmp/test.txt | xargs -r -d'\n' -I {} echo "test '{}'"
正如 Stéphane Chazelas 所澄清的, -r 不会过滤空输入,但会在没有输入时阻止运行。
来自 xargs 手册页 -r, --no-run-if-empty 部分:
通常,即使没有输入,该命令也会运行一次。
从进一步编辑中获得的附加说明,-0 和 -d 是相互冲突的参数,后者优先。