如果stdin输入为空,如何忽略xargs命令?


188

考虑以下命令:

ls /mydir/*.txt | xargs chown root

目的是将所有文本文件的所有者更改mydir为root

问题是,如果其中没有.txt文件,mydir则xargs会引发错误,指出未指定路径。这是一个无害的示例,因为会引发错误,但是在某些情况下,例如在我需要在此处使用的脚本中,假定空白路径为当前目录。因此,如果我/home/tom/从那时开始运行该命令,如果没有结果,ls /mydir/*.txt并且所有文件/home/tom/的所有者都更改为root。

那么,如何让xargs忽略空结果?


6
另外:切勿通过管道输出ls用于程序设计;参见mywiki.wooledge.org/ParsingLs
Charles Duffy

我的用例是git branch --merged | grep -v '^* ' | xargs git branch -d,它在输入为空时也会失败
belkka

Answers:


305

对于GNU xargs,您可以使用-r--no-run-if-empty选项:

--no-run-if-empty
-r
如果标准输入不包含任何非空格,请不要运行该命令。通常,即使没有输入,命令也会运行一次。此选项是GNU扩展。


9
老实说,我首先在Google中进行搜索,然后发现了这一点(但如果不阅读手册就不会提出要求)。我认为这应该是默认行为,不需要启用开关即可。
JonnyJD 2013年

28
不幸的是,这在Mac上不起作用。无论是短期还是长期选择。
wedi 2015年

9
@wedi-OSX具有BSD用户空间,因此这种情况经常发生。您可以使用自制软件安装GNU fileutils来解决此问题。
edk750 '16

7
你是说brew install findutilsfindutils不是fileutils
Mitar

3
在macOS上,不提供标志将导致无论如何都无法运行命令,因此我猜它是不需要的。
Trejkaz

15

非GNU的xargs用户可以利用的-L <#lines>-n <#args>-i,和-I <string>

ls /empty_dir/ | xargs -n10 chown root # chown executed every 10 args or fewer
ls /empty_dir/ | xargs -L10 chown root # chown executed every 10 lines or fewer
ls /empty_dir/ | xargs -i cp {} {}.bak # every {} is replaced with the args from one input line
ls /empty_dir/ | xargs -I ARG cp ARG ARG.bak # like -i, with a user-specified placeholder

请记住,xargs在空格处分隔行,但可以使用引号和转义。有关详细信息,请参见RTFM。

而且,正如Doron Behar所提到的,此解决方法不是可移植的,因此可能需要检查:

$ uname -is
SunOS sun4v
$ xargs -n1 echo blah < /dev/null
$ uname -is
Linux x86_64
$ xargs --version | head -1
xargs (GNU findutils) 4.7.0-git
$ xargs -n1 echo blah < /dev/null
blah

2
似乎没有回答这个问题?
Nicolas Raoul 2014年

2
@Nicolas:如果您的的版本xargs不支持该--no-run-if-empty开关并尝试在没有STDIN输入的情况下尝试运行命令参数,则这是阻止执行的方法(在Solaris 10中就是这种情况)。其他Unix中的版本可能只是忽略一个空列表(例如AIX)。
arielCo 2014年

4
是! 在MAC OSX上, echo '' | xargs -n1 echo blah不执行任何操作。而echo 'x' | xargs -n1 echo blah打印“等等”。
carlosayam

2
对于GNU和BSD / OSX的支持,我最终都会使用类似:的方法ls /mydir/*.txt | xargs -n 1 -I {} chown root {},如这个答案所示。
路易斯Bianchin的

2
应当注意,使用GNU的进行echo '' | xargs -n1 echo blah打印。blahxargs
多伦·比哈

11

man xargs--no-run-if-empty


1
如果您使用的是OSX,则不会。如果您好奇为什么,edk750会在他们的评论中解释这一点。
jasonleonhard

9

在方面xargs,您可以-r按建议使用,但是BSD不支持xargs

因此,作为解决方法,您可以传递一些额外的临时文件,例如:

find /mydir -type f -name "*.txt" -print0 | xargs -0 chown root $(mktemp)

或将其stderr重定向为null(2> /dev/null),例如

find /mydir -type f -name "*.txt" -print0 | xargs -0 chown root 2> /dev/null || true

另一个更好的方法是使用while循环遍历找到的文件:

find /mydir -type f -name "*.txt" -print0 | while IFS= read -r -d '' file; do
  chown -v root "$file"
done

另请参阅:在Mac OS X中忽略xargs的空结果


另外请注意,更改权限的方法不好,不建议使用。绝对不应该解析ls命令的输出(请参阅:为什么不应该解析ls的输出)。尤其是当您以root用户身份运行命令时,由于文件中可能包含特殊字符,这些特殊字符可能会被shell解释,或者想象文件中带有空格字符/,那么结果可能会很糟糕。

因此,您应该更改方法并改用find命令,例如

find /mydir -type f -name "*.txt" -execdir chown root {} ';'

1
抱歉,我不了解的while IFS= read -r -d '' file有效性。你可以解释吗?
阿德里安

2

在OSX上:xargs处理-r参数的Bash重新实现,将其放入,$HOME/bin然后将其添加到中PATH

#!/bin/bash
stdin=$(cat <&0)
if [[ $1 == "-r" ]] || [[ $1 == "--no-run-if-empty" ]]
then
    # shift the arguments to get rid of the "-r" that is not valid on OSX
    shift
    # wc -l return some whitespaces, let's get rid of them with tr
    linecount=$(echo $stdin | grep -v "^$" | wc -l | tr -d '[:space:]') 
    if [ "x$linecount" = "x0" ]
    then
      exit 0
    fi
fi

# grep returns an error code for no matching lines, so only activate error checks from here
set -e
set -o pipefail
echo $stdin | /usr/bin/xargs $@

2

这是GNU xargs的行为,可以通过使用-r,--no-run-if-empty来抑制。

xargs的* BSD变体默认情况下具有此行为,因此不需要-r。从FreeBSD 7.1(2009年1月发布)开始,出于兼容性原因,接受-r参数(witch不做任何事情)。

我个人更喜欢在脚本中使用longopts,但是由于* BSD xargs不使用longopts,只需使用“ -r”并且xargs在* BSD和Linux系统上的行为相同

可悲的是,MacOS(当前为MacOS Mojave)上的xargs不支持“ -r”参数。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.