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

时间:2011-11-28 13:46:54

标签: bash centos xargs

考虑这个命令:

ls /mydir/*.txt | xargs chown root

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

问题是,如果.txt中没有mydir个文件,则xargs会发出错误消息,指出没有指定路径。这是一个无害的例子,因为抛出了错误,但在某些情况下,就像我需要在这里使用的脚本一样,空白路径被假定为当前目录。因此,如果我从/home/tom/运行该命令,那么如果ls /mydir/*.txt没有结果,/home/tom/下的所有文件都将其所有者更改为root。

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

7 个答案:

答案 0 :(得分:250)

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

  

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

答案 1 :(得分:12)

非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 :(得分:9)

man xargs--no-run-if-empty

答案 3 :(得分:8)

xargs而言,您可以按照建议使用-r,但BSD xargs不支持。{/ p>

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

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

另请参阅:Ignore empty results for xargs in Mac OS X

另请注意,您更改权限的方法并不好,不鼓励使用。当然你不应该解析ls command的输出(参见:Why you shouldn't parse the output of ls)。特别是当您通过root运行命令时,因为您的文件可以包含可由shell解释的特殊字符,或者想象/周围有空格字符的文件,那么结果可能很糟糕。

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

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

答案 4 :(得分:2)

在OSX上:Bash重新实现处理xargs参数的-r,例如,在$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 $@

答案 5 :(得分:0)

这是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”参数。

答案 6 :(得分:0)

使用 -r/--no-run-if-empty xargs 参数的跨平台(Mac 和 Linux)替代方法:

带有空参数的示例(在 Ubuntu 18.04 和 Big Sur 上的结果相同):

$ echo | xargs -I {}  echo "This is a test with '{}'"
$
$
$ cat /dev/null | xargs -I {}  echo "This is a test with '{}'"
$

多行示例:

$ seq 1 5  | xargs -I {}  echo "This is a test with '{}'"
This is a test with '1'
This is a test with '2'
This is a test with '3'
This is a test with '4'
This is a test with '5'
$