bash 如果标准输入为空,如何忽略 xargs 命令?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8296710/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 21:15:21  来源:igfitidea点击:

How to ignore xargs commands if stdin input is empty?

bashcentosxargs

提问by HyderA

Consider this command:

考虑这个命令:

ls /mydir/*.txt | xargs chown root

The intention is to change owners of all text files in mydirto root

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

The issue is that if there are no .txtfiles in mydirthen xargs thows an error saying there is no path specified. This is a harmless example because an error is being thrown, but in some cases, like in the script that i need to use here, a blank path is assumed to be the current directory. So if I run that command from /home/tom/then if there is no result for ls /mydir/*.txtand all files under /home/tom/have their owners changed to root.

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

So how can I have xargs ignore an empty result?

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

回答by Sven Marnach

For GNU xargs, you can use the -ror --no-run-if-emptyoption:

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

--no-run-if-empty
-r
If the standard input does not contain any nonblanks, do not run the command. Normally, the command is run once even if there is no input. This option is a GNU extension.

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

回答by arielCo

Users of non-GNU xargs may take advantage of -L <#lines>, -n <#args>, -i, and -I <string>:

非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

Keep in mind that xargs splits the line at whitespace but quoting and escaping are available; RTFM for details.

请记住, xargs 在空格处拆分行,但可以使用引用和转义;RTFM 了解详情。

Also, as Doron Behar mentions, this workaround isn't portable so checks may be needed:

此外,正如 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

回答by thiton

man xargssays --no-run-if-empty.

man xargs--no-run-if-empty

回答by kenorb

In terms of xargs, you can use -ras suggested, however it's not supported by BSD xargs.

就 而言xargs,您可以-r按照建议使用,但是 BSD 不支持它xargs

So as workaround you may pass some extra temporary file, for example:

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

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

or redirect its stderr into null (2> /dev/null), e.g.

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

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


Another better approach is to iterate over found files using whileloop:

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

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

See also: Ignore empty results for xargs in Mac OS X

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



Also please note that your method of changing the permissions isn't great and it's discouraged. Definitely you shouldn't parse output of lscommand(see: Why you shouldn't parse the output of ls). Especially when you're running your command by root, because your files can consist special characters which may be interpreted by the shell or imagine the file having a space character around /, then the results can be terrible.

另请注意,您更改权限的方法不是很好,不鼓励这样做。绝对不应该解析ls命令的输出(请参阅:为什么不应该解析 ls 的输出)。特别是当您通过 root 运行命令时,因为您的文件可能包含特殊字符,这些字符可能会被 shell 解释,或者想象文件周围有一个空格字符/,那么结果可能会很糟糕。

Therefore you should change your approach and use findcommand instead, e.g.

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

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

回答by rcomblen

On OSX: Bash reimplementation of xargsdealing with the -rargument, put that e.g. in $HOME/binand add it to the PATH:

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

#!/bin/bash
stdin=$(cat <&0)
if [[  == "-r" ]] || [[  == "--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 $@

回答by Mirko Steiner

This is a behaviour of GNU xargs which can be supressed by using -r, --no-run-if-empty.

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

The *BSD variant of xargs has this behavtheitroad on default, so -r is not needed. Since FreeBSD 7.1 (released in january 2009) an -r argument is accepted (witch does nothing) for compatiblity reasons.

xargs 的 *BSD 变体在默认情况下具有此行为,因此不需要 -r。自 FreeBSD 7.1(2009 年 1 月发布)以来,出于兼容性原因,接受了 -r 参数(witch 什么也不做)。

I personally prefer using longopts in scripts but since the *BSD xargs does not uses longopts just use "-r" and xargs will act the same on *BSD an on linux systems

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

xargs on MacOS (currently MacOS Mojave) sadly don't supports the "-r" argument.

遗憾的是,MacOS(目前是 MacOS Mojave)上的 xargs 不支持“-r”参数。