243

次のコマンドについて考えてみます。

ls /mydir/*.txt | xargs chown root

目的は、すべてのテキストファイルの所有者mydirをrootに変更することです

.txt問題は、ファイルがない場合mydir、xargsはパスが指定されていないというエラーを表示することです。エラーがスローされているため、これは無害な例ですが、場合によっては、ここで使用する必要のあるスクリプトのように、空白のパスが現在のディレクトリであると見なされます。したがって、それ以降にそのコマンドを実行すると/home/tom/、結果がなく、ls /mydir/*.txt下のすべてのファイル/home/tom/の所有者がルートに変更されます。

では、xargsに空の結果を無視させるにはどうすればよいですか?

4

7 に答える 7

413

GNUの場合、 orオプションxargsを使用できます。-r--no-run-if-empty

--no-run-if-empty
-r
標準入力に非ブランクが含まれていない場合は、コマンドを実行しないでください。通常、コマンドは入力がなくても1回実行されます。このオプションはGNU拡張機能です。

macOSで使用されるBSDバージョンのはxargs空の入力に対してコマンドを実行しないため、この-rオプションは必要ありません(また、そのバージョンのでは受け入れられませんxargs)。

于 2011-11-28T13:49:10.600 に答える
17

非GNUxargsのユーザーは、、、、-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。

また、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
于 2013-09-26T21:08:58.473 に答える
11

man xargsと言い--no-run-if-emptyます。

于 2011-11-28T13:48:09.127 に答える
10

に関しては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

もう1つの優れたアプローチは、whileループを使用して見つかったファイルを反復処理することです。

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

参照:MacOSXでxargsの空の結果を無視する


また、権限を変更する方法は優れていないため、お勧めできません。間違いなく、lsコマンドの出力を解析するべきではありません(「 lsの出力を解析すべきでない理由」を参照)。特にrootでコマンドを実行している場合、ファイルはシェルによって解釈される可能性のある特殊文字で構成されているか、ファイルの周囲/にスペース文字があると想像される可能性があるため、結果はひどいものになる可能性があります。

したがって、アプローチを変更し、find代わりにコマンドを使用する必要があります。

find /mydir -type f -name "*.txt" -execdir chown root {} ';'
于 2016-05-13T21:23:02.530 に答える
5

-r/ --no-run-if-emptyxargsのパラメーターを使用する代わりのクロスプラットフォーム(MacおよびLinux) :

空のパラメーターを使用した例(Ubuntu18.04とBigSurで同じ結果):

$ 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'
$
于 2021-02-08T17:56:14.520 に答える
3

これはGNUxargsの動作であり、-r、-no-run-if-emptyを使用して抑制できます。

xargsの*BSDバリアントにはデフォルトでこの動作があるため、-rは必要ありません。FreeBSD 7.1(2009年1月にリリース)以降、互換性の理由から-r引数が受け入れられます(witchは何もしません)。

私は個人的にスクリプトでlongoptsを使用することを好みますが、* BSD xargsはlongoptsを使用しないため、「-r」を使用するだけで、xargsはLinuxシステムの*BSDでも同じように機能します。

MacOS(現在のMacOS Mojave)のxargsは、残念ながら「-r」引数をサポートしていません。

于 2018-11-28T14:42:47.980 に答える
2

OSXの場合:引数xargsを処理するためのBashの再実装、たとえばそれを入れて:に追加します。-r$HOME/binPATH

#!/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 $@
于 2017-05-15T06:39:29.273 に答える