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
How to ignore xargs commands if stdin input is empty?
提问by HyderA
Consider this command:
考虑这个命令:
ls /mydir/*.txt | xargs chown root
The intention is to change owners of all text files in mydir
to root
目的是将所有文本文件的所有者更改mydir
为 root
The issue is that if there are no .txt
files in mydir
then 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/*.txt
and 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 -r
or --no-run-if-empty
option:
对于 GNU xargs
,您可以使用-r
or--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 xargs
says --no-run-if-empty
.
man xargs
说--no-run-if-empty
。
回答by kenorb
In terms of xargs
, you can use -r
as 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 while
loop:
另一种更好的方法是使用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 ls
command(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 find
command instead, e.g.
因此你应该改变你的方法并使用find
命令,例如
find /mydir -type f -name "*.txt" -execdir chown root {} ';'
回答by rcomblen
On OSX: Bash reimplementation of xargs
dealing with the -r
argument, put that e.g. in $HOME/bin
and 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”参数。