在OsX下,与xargs -r相当的命令是什么?

11

在OSX下有没有与Linux下的xargs -r相当的类似命令?我正在尝试找到一种在没有数据的情况下中断管道的方法。

例如,假设你执行以下操作:

touch test
cat test | xargs -r echo "content: "

那样做没有结果,因为xargs会打断管道。

在OSX下是否有一些隐藏的xargs选项或其他方式可以达到相同的效果?

8个回答

3
POSIX标准中关于xargs的规定是,即使没有参数,该命令也必须执行一次。这很麻烦,这就是为什么GNU xargs-r选项的原因。不幸的是,BSD(MacOS X)和其他主流Unix版本(AIX、HP-UX、Solaris)都不支持它。
如果这对你很重要,请在环境可以找到它的地方获取并安装GNU xargs,而不影响系统(因此不要替换/usr/bin/xargs,除非你比我更勇敢——但/usr/local/bin/xargs可能没问题,或者$HOME/bin/xargs,或者……)。

2
您可以使用test[
if [ -s test ] ; then cat test | xargs echo content: ; fi

谢谢你的回答!在我的情况下,作为“_cat test_”实际上是在命令行中执行一系列命令,更加复杂。但是分步骤执行的想法是正确的方法。我想我太过于沉迷于“单行综合症”了 ;) - Alex

2

判断正在运行的xargs是否为GNU或非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.'

1
我之前没有注意到BSD/macOS的xargs即使没有-r选项的支持,也能够正常运行,但是在我的Mac上,xargs ls < /dev/null没有任何输出;它的行为就像已经指定了-r选项一样。 - Jonathan Leffler
为什么是 cat - Stephane Chazelas
为什么要使用三个引号?据我所知,"$@""""$@"""之间唯一的区别在于,如果向portable_xargs_r传递了零个参数,则后者会导致xargs被传递一个空参数,从而防止其运行默认的echo命令。 - Stephane Chazelas
为什么 cats:cat foo 已被删除。它们只是为了保持并行性。cat - 在可移植的方式中表示“cat标准输入”,其他shell可能也有适用的简写形式。 - TomOnTime
为什么三引号:好发现。它们是不必要的,已经被删除了。 - TomOnTime

1

这是一个快速而简单的 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"

1

使用 POSIX xargs¹,为了避免在输入为空时运行 the-command,您可以使用 moreutilsifne(表示如果不为空):

... | ifne xargs ... the-command ...

或者使用一个检查参数数量的sh包装器:

... | xargs ... sh -c '[ "$#" -eq 0 ] || exec the-command ... "$@"' sh

¹虽然人们几乎不能在POSIX上使用xargs,因为它不支持-0,当输入是非文本时(例如文件名,在大多数系统上除了POSIX语言环境外不能保证是文本),以非常晦涩的方式解析其输入,并且这是与语言环境相关的,而且如果任何单词超过255个字节长度,它不会给出任何保证!


0

一个典型的使用案例如下:

find . -print0 | xargs -r -0 grep PATTERN

某些版本的xargs没有-r标志。在这种情况下,您可以将/dev/null作为第一个文件名提供,以便grep永远不会被交付一个空的文件名列表。由于该模式永远不会在/dev/null中找到,因此这不会影响输出:

find . -print0 | xargs  -0 grep PATTERN /dev/null

这里不是一个好的grep示例。如果没有传递任何参数给grep,则它将查找其stdin,具体取决于xargs实现,可能会打开/dev/null或来自find的(空)管道,因此在这里省略-r是无害的(只要grep未传递非标准的-L/-r/-R等选项)。 - Stephane Chazelas

0
你可以确保输入总是至少有一行。这可能并不总是可行的,但你会惊讶于有多少创造性的方法可以做到这一点。

1
-r 的作用是在没有输入行的情况下 阻止 执行命令。 - Thomas
因此,“echo”是在“xargs -r”下运行的一个很差的命令示例,因为它相当无害。更常见的用例是避免在管道到“xargs kill”或类似命令时出现错误,或者当然,适当昂贵的操作和危及数据完整性的操作仍然是更好的示例。 - tripleee

0

您可以测试流是否有任何内容:

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中运行管道内的检查,因此可以在复杂的管道设置中有效地使用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接