【问题标题】:How to ignore xargs commands if stdin input is empty?如果标准输入为空,如何忽略 xargs 命令?
【发布时间】:2012-01-07 23:07:09
【问题描述】:

考虑这个命令:

ls /mydir/*.txt | xargs chown root

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

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

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

【问题讨论】:

  • 旁白:永远不要将来自ls 的输出用于编程用途;见mywiki.wooledge.org/ParsingLs
  • 我的用例是git branch --merged | grep -v '^* ' | xargs git branch -d,在空输入时也会失败
  • 我觉得你可以跑git branch --merged | grep -v '^* ' | xargs --no-run-if-empty git branch -d

标签: bash centos xargs


【解决方案1】:

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

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

在 macOS 上使用的 xargs 的 BSD 版本不会针对空输入运行命令,因此不需要 -r 选项(该版本的 xargs 也不接受它)。

【讨论】:

  • 老实说,我首先在谷歌搜索并找到了这个(但如果不阅读手册就不会问)。我有点认为这应该是默认行为,不需要开关来启用它。
  • 不幸的是,这在 Mac 上不起作用。短期或长期选项都不是。
  • @wedi - OSX 有 BSD 用户空间,所以这种事情会经常发生。您可以通过使用自制软件安装 GNU fileutils 来解决它。
  • 你的意思是brew install findutilsfindutils,不是fileutils
  • 在 macOS 上,不提供标志会导致无论如何都不会运行命令,所以我想它只是不需要。
【解决方案2】:

非 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

【讨论】:

  • 似乎没有回答问题?
  • @Nicolas:如果您的 xargs 版本不支持 --no-run-if-empty 开关并尝试在没有 STDIN 输入的情况下运行命令参数(在 Solaris 10 中就是这种情况),这是阻止执行的方法)。其他 unix 中的版本可能会忽略一个空列表(例如 AIX)。
  • 是的!在 MAC OSX 上,echo '' | xargs -n1 echo blah 什么都不做;而echo 'x' | xargs -n1 echo blah 打印“blah”。
  • 对于 GNU 和 BSD/OSX 支持,我最终会使用类似:ls /mydir/*.txt | xargs -n 1 -I {} chown root {},正如这个答案所暗示的那样。
  • 需要注意的是echo '' | xargs -n1 echo blah打印blah与GNU的xargs
【解决方案3】:

man xargs--no-run-if-empty

【讨论】:

  • 如果你在 OSX 上它不会。如果您好奇为什么,edk750 在他们的评论中解释了这一点。
【解决方案4】:

对于xargs,您可以按照建议使用-r,但BSD不支持xargs

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

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

或将其 stderr 重定向为 null (2&gt; /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 {} ';'

【讨论】:

  • 抱歉,我不明白while IFS= read -r -d '' file 的有效性。你能解释一下吗?
  • 最新的 macOS BSD 版本接受-r,但忽略它,因为无论如何它在没有输入的情况下什么都不做。
【解决方案5】:

使用-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'
$

【讨论】:

  • xargs: warning: options --replace and -L are mutually exclusive, ignoring previous --replace value 这就是我使用 GNU xargs 得到的结果
  • 正如我所提到的,这是一个跨平台的替代方案。 MacOS catalina 的 xargs 不提供 -r / --replace 参数。
  • 不。那是错误的。试试cat /dev/null | xargs -I {} -L1 echo {} ...虽然它可以在 macOS 上运行,但在 Linux 上却不行。
  • @tcurdt 确实,我的回答是错误的。使用正确的参数对其进行了更新,并针对 ubuntu 18.04 和 Big Sur 检查了我的解决方案。谢谢。
  • 你的例子仍然是错误的。应该是cat /dev/null | xargs -I {} echo "This is a test with '{}'"echo hello | xargs -I {} echo "This is a test with '{}'" This is a test with 'hello'
【解决方案6】:

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

【讨论】:

    【解决方案7】:

    在 OSX 上:xargs 的 Bash 重新实现处理 -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 $@
    

    【讨论】:

      猜你喜欢
      • 2021-01-23
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-28
      • 1970-01-01
      • 2016-06-02
      相关资源
      最近更新 更多