忽略xargs的空结果
考虑这个命令:
ls /mydir/*.txt | xargs chown root
目的是将mydir
中所有文本文件的所有者更改为root
问题是,如果在mydir
中没有.txt
文件,那么xargs会报错,说没有指定path。 这是一个无害的例子,因为正在抛出一个错误,但在某些情况下,就像我在这里需要使用的脚本一样,一个空白path被假定为当前目录。 因此,如果我从/home/tom/
运行该命令,那么如果ls /mydir/*.txt
没有结果,并且/home/tom/
下的所有文件都将其所有者更改为root。
那么我怎么能让xargs忽略一个空的结果呢?
对于GNU xargs
,可以使用-r
或--no-run-if-empty
选项:
--no-run-if-empty
-r
如果标准input不包含任何nonblanks,请不要运行该命令。 通常情况下,即使没有input,该命令也会运行一次。 这个选项是一个GNU扩展。
man xargs
说--no-run-if-empty
。
非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的细节。
就xargs
,您可以按照build议使用-r
,但是不支持BSD xargs
。
所以作为解决方法,您可能会传递一些额外的临时文件,例如:
find /mydir -type f -name "*.txt" -print0 | xargs -0 chown root $(mktemp)
或将其stderrredirect到null( 2> /dev/null
),例如
find /mydir -type f -name "*.txt" -print0 | xargs -0 chown root 2> /dev/null || true
另一个更好的方法是使用while
循环迭代find的文件:
find /mydir -type f -name "*.txt" -print0 | while IFS= read -r -d '' file; do chown -v root "$file" done
另请参阅: 在Mac OS X中忽略xargs的空结果
另外请注意,您更改权限的方法不是很好,也不鼓励。 当然你不应该parsingls
命令的输出(参见: 为什么你不应该parsingls的输出 )。 特别是当你用root运行你的命令时,因为你的文件可能包含特殊字符,这些特殊字符可能被shell解释,或者想像文件在/
周围有一个空格字符,那么结果可能会很糟糕。
因此,你应该改变你的方法,而不是使用find
命令,例如
find /mydir -type f -name "*.txt" -execdir chown root {} ';'
在OSX上:重新实现处理-r
参数的xargs
,把它放在$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 $@