【发布时间】:2012-02-06 21:45:42
【问题描述】:
它们在 OSX 下是否与 Linux 下的 xargs -r 等效?如果没有数据,我正在尝试找到一种方法来中断管道。
例如,假设您执行以下操作:
touch test
cat test | xargs -r echo "content: "
这不会产生任何结果,因为 xargs 会中断管道。
是否有一些 hidden xargs 选项或其他东西可以在 OSX 下达到相同的结果?
【问题讨论】:
它们在 OSX 下是否与 Linux 下的 xargs -r 等效?如果没有数据,我正在尝试找到一种方法来中断管道。
例如,假设您执行以下操作:
touch test
cat test | xargs -r echo "content: "
这不会产生任何结果,因为 xargs 会中断管道。
是否有一些 hidden xargs 选项或其他东西可以在 OSX 下达到相同的结果?
【问题讨论】:
您可以使用test 或[:
if [ -s test ] ; then cat test | xargs echo content: ; fi
【讨论】:
xargs 的 POSIX 标准要求命令执行一次,即使没有参数也是如此。这是一个麻烦,这就是为什么 GNU xargs 有 -r 选项。不幸的是,无论是 BSD (MacOS X) 还是其他主流 Unix 版本(AIX、HP-UX、Solaris)都不支持它。
如果它对您很重要,请在您的环境可以找到它的地方获取并安装 GNU xargs,而不影响系统(所以不要替换 /usr/bin/xargs,除非您比我更勇敢——但是/usr/local/bin/xargs 可能没问题,或者$HOME/bin/xargs,或者……)。
【讨论】:
没有标准的方法来确定您正在运行的 xargs 是否是 GNU。我将$gnuargs 设置为“true”或“false”,然后使用一个函数替换 xargs 并执行正确的操作。
在 Linux、FreeBSD 和 MacOS 上,此脚本适用于我。 xargs 的 POSIX 标准要求命令执行一次,即使没有参数也是如此。 FreeBSD 和 MacOS X 违反了这条规则,因此不需要“-r”。 GNU 觉得它很烦人,并添加了-r。这个脚本做的是正确的事,如果你找到一个以其他方式做的 Unix 版本,它可以被增强。
#!/bin/bash
gnuxargs=$(xargs --version 2>&1 |grep -s GNU >/dev/null && echo true || echo false)
function portable_xargs_r() {
if $gnuxargs ; then
cat - | xargs -r "$@"
else
cat - | xargs "$@"
fi
}
echo 'this' > foo
echo '=== Expect one line'
portable_xargs_r <foo echo "content: "
echo '=== DONE.'
cat </dev/null > foo
echo '=== Expect zero lines'
portable_xargs_r <foo echo "content: "
echo '=== DONE.'
【讨论】:
-r 选项的支持,BSD/macOS xargs 也能正常工作,但 xargs ls < /dev/null 在我的 Mac 上不会产生任何东西;它的行为就像指定了 -r。
cats?
"$@" 和 """$@""" 之间的唯一区别在于后者,如果 portable_xargs_r 未传递任何参数,xargs 最终将传递一个空参数,因此会阻止它运行默认 @ 987654333@ 命令。
cats:cat foo 已被删除。他们在那里只是为了保持平行。 cat - 以可移植方式表示“cat 标准输入”,其他 shell 可能也有可用的简写。
您可以确保输入始终至少有一行。这可能并不总是可行,但您会惊讶于有多少创造性的方法可以做到这一点。
【讨论】:
-r的意义在于防止在没有输入行的情况下运行命令。
echo 是在xargs -r 下运行的命令的一个糟糕示例,因为它非常无害。一个更常见的用例是在管道到xargs kill 或类似的东西时避免错误,或者当然是适当昂贵的操作以及损害数据完整性的操作仍然是更好的例子。
这是一个使用临时文件的快速而肮脏的xargs-r。
#!/bin/sh
t=$(mktemp -t xargsrXXXXXXXXX) || exit
trap 'rm -f $t' EXIT HUP INT TERM
cat >"$t"
test -s "$t" || exit
exec xargs "$@" <"$t"
【讨论】:
一个典型的用例如下所示:
find . -print0 | xargs -r -0 grep PATTERN
某些版本的 xargs 没有-r 标志。在这种情况下,您可以提供 /dev/null 作为第一个文件名,以便 grep 永远不会收到一个空的文件名列表。由于模式永远不会在 /dev/null 中找到,这不会影响输出:
find . -print0 | xargs -0 grep PATTERN /dev/null
【讨论】:
grep 不是一个很好的例子。如果grep 没有传递任何参数,那么它将查看其标准输入,这取决于xargs 实现将在/dev/null 或find 的(空)管道上打开,因此省略-r 将在这里是无害的(只要grep没有通过非标准-L/-r/-R...选项)。
使用 POSIX xargs¹,为避免在输入为空时运行 the-command,您可以使用 moreutils 的 ifne(对于 如果不为空):
... | ifne xargs ... the-command ...
或者使用检查参数数量的sh 包装器:
... | xargs ... sh -c '[ "$#" -eq 0 ] || exec the-command ... "$@"' sh
¹ 虽然一个人几乎不能使用xargs POSIXly,因为它不支持-0,但当输入为非文本时具有未指定的行为(例如对于大多数系统上不保证为文本的文件名)除了在 POSIX 语言环境中),它以一种非常神秘的方式解析其输入,这取决于语言环境,并且不保证任何单词的长度是否超过 255 个字节!
【讨论】:
您可以测试流是否有任何内容:
cat test | { if IFS= read -r tmp; then { printf "%s\n" "$tmp"; cat; } | xargs echo "content: "; fi; }
# ^^^ - otherwise just do nothing
# ^^^^^^^^^^^^^^^^^^^^^^^ - to xargs
# ^^^ - and the rest of input
# ^^^^^^^^^^^^^^^^^^^^^^ - redirect first line
# ^^^^^^^^^^^^^^^^^^^ - try reading anything
# or with a function
# even TODO: add the check of `portable_xargs_r` in the other answer and call `xargs -r` when available.
xargs_r() {
if IFS= read -r tmp; then
{ printf "%s\n" "$tmp"; cat; } | xargs "$@"
fi
}
cat test | xargs_r echo "content: "
此方法在子shell 内的管道内运行检查,因此它可以有效地用于复杂的管道设置。
【讨论】: