考虑以下命令:
ls /mydir/*.txt | xargs chown root
目的是将mydir中所有文本文件的所有者更改为root
问题是,如果mydir中没有.txt文件,那么xargs就会出现一个错误,说没有指定路径。这是一个无害的例子,因为会抛出一个错误,但在某些情况下,比如在我需要在这里使用的脚本中,会假设一个空白路径是当前目录。因此,如果我从/home/tom/运行该命令,那么如果ls /mydir/*.txt没有结果,那么/home/tom/下的所有文件都将其所有者更改为root。
如何让xargs忽略空结果呢?
对于xargs,您可以按照建议使用-r,但是BSD xargs不支持它。
所以作为解决方案,你可以传递一些额外的临时文件,例如:
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
请参见:在Mac OS X中忽略xargs的空结果
还请注意,您更改权限的方法不是很好,不鼓励这样做。当然你不应该解析ls命令的输出(参见:为什么你不应该解析ls的输出)。特别是当您以根用户运行命令时,因为您的文件可能包含特殊字符,这些字符可能由shell解释,或者想象文件在/周围有一个空格字符,那么结果可能会很糟糕。
因此,你应该改变你的方法,用find命令代替。
find /mydir -type f -name "*.txt" -execdir chown root {} ';'
对于xargs,您可以按照建议使用-r,但是BSD xargs不支持它。
所以作为解决方案,你可以传递一些额外的临时文件,例如:
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
请参见:在Mac OS X中忽略xargs的空结果
还请注意,您更改权限的方法不是很好,不鼓励这样做。当然你不应该解析ls命令的输出(参见:为什么你不应该解析ls的输出)。特别是当您以根用户运行命令时,因为您的文件可能包含特殊字符,这些字符可能由shell解释,或者想象文件在/周围有一个空格字符,那么结果可能会很糟糕。
因此,你应该改变你的方法,用find命令代替。
find /mydir -type f -name "*.txt" -execdir chown root {} ';'
一个跨平台的(Mac和Linux)替代使用-r/——no-run-if-empty xargs的参数:
参数为空的示例(在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'
$