如果 stdin 输入为空,如何忽略 xargs 命令?

发布于 2024-12-18 17:54:09 字数 422 浏览 5 评论 0原文

考虑这个命令:

ls /mydir/*.txt | xargs chown root

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

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

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

Consider this command:

ls /mydir/*.txt | xargs chown root

The intention is to change owners of all text files in mydir to 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.

So how can I have xargs ignore an empty result?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

叹沉浮 2024-12-25 17:54:09

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

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

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

For GNU xargs, you can use the -r or --no-run-if-empty option:

--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.

The BSD version of xargs, which is used on macOS, does not run the command for an empty input, so the -r option is not needed (nor is it accepted by that version of xargs).

御弟哥哥 2024-12-25 17:54:09

非 GNU xargs 的用户可以利用 -L <#lines>-n <#args>-i、和 -I

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

Users of non-GNU xargs may take advantage of -L <#lines>, -n <#args>, -i, and -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.

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

$ 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
痴情 2024-12-25 17:54:09

man xargs--no-run-if-empty

man xargs says --no-run-if-empty.

謌踐踏愛綪 2024-12-25 17:54:09

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 的输出)。特别是当您以 root 身份运行命令时,因为您的文件可能包含可由 shell 解释的特殊字符,或者想象文件在 / 周围有空格字符,那么结果可能会很糟糕。

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

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

In terms of xargs, you can use -r as suggested, however it's not supported by 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.

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:

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


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.

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

find /mydir -type f -name "*.txt" -execdir chown root {} ';'
花心好男孩 2024-12-25 17:54:09

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

A cross-platform (Mac and Linux) alternative to using -r/--no-run-if-empty xargs' parameter:

Example with empty parameters (same result on Ubuntu 18.04 and Big Sur):

$ echo | xargs -I {}  echo "This is a test with '{}'"
$
$
$ cat /dev/null | xargs -I {}  echo "This is a test with '{}'"
$

Example with multiple lines:

$ 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'
$
等数载,海棠开 2024-12-25 17:54:09

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

xargs 的 *BSD 变体默认具有此行为,因此不需要 -r。自 FreeBSD 7.1(2009 年 1 月发布)以来,出于兼容性原因,接受 -r 参数(witch 不执行任何操作)。

我个人更喜欢在脚本中使用 longopts,但由于 *BSD xargs 不使用 longopts,只需使用“-r”,并且 xargs 在 *BSD 和

MacOS 上的 Linux 系统(当前为 MacOS Mojave)上的 xargs 的行为相同,遗憾的是不支持“-r”参数。

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

The *BSD variant of xargs has this behavoir 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.

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

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

夜吻♂芭芘 2024-12-25 17:54:09

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

#!/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 $@

On OSX: Bash reimplementation of xargs dealing with the -r argument, put that e.g. in $HOME/bin and add it to the 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 $@
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文