stdin入力が空の場合、xargsコマンドを無視するにはどうすればよいですか?


188

次のコマンドを検討してください。

ls /mydir/*.txt | xargs chown root

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

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

それで、どうすればxargsに空の結果を無視させることができますか?


6
余談:lsプログラムで使用するためにからの出力をパイプしないでください。mywiki.wooledge.org/ParsingLsを
Charles Duffy

私の使用例はでgit branch --merged | grep -v '^* ' | xargs git branch -d、空の入力でも失敗します
belkka

回答:


305

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

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


9
私は最初にグーグルで正直に検索し、これを見つけました(しかし、マニュアルを読まなければ尋ねませんでした)。これはデフォルトの動作であり、有効にするためのスイッチを必要としないと思いました。
JonnyJD 2013年

28
残念ながら、これはMacでは機能しません。ショートオプションもロングオプションもありません。
wedi

9
@wedi-OSXにはBSDユーザースペースがあるため、このようなことが頻繁に発生します。あなたはhomebrewを使ってGNU fileutilsをインストールすることでそれを回避することができます。
edk750 2016

7
つまりbrew install findutilsfindutils、ではありませんfileutils
Mitar

3
macOSでは、フラグを指定しないと、とにかくコマンドが実行されないので、必要ないだけだと思います。
Trejkaz 2018年

15

非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。

また、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

2
質問に答えていないようですか?
Nicolas Raoul 2014

2
@Nicolas:のバージョンがスイッチをxargsサポート--no-run-if-emptyせず、STDIN入力なしでコマンド引数を実行しようとした場合に実行を防ぐ方法です(これはSolaris 10の場合です)。他のUNIXのバージョンは、空のリスト(AIXなど)を無視するだけです。
arielCo 14

4
はい!MAC OSX echo '' | xargs -n1 echo blahでは何もしません。一方、echo 'x' | xargs -n1 echo blah「何とか」を出力します。
carlosayam 2017年

2
GNUとBSD / OSXの両方のサポートls /mydir/*.txt | xargs -n 1 -I {} chown root {}について、この回答が示唆するように、私は最終的に次のようなものを使用します。
ルイスビアン

2
GNU でecho '' | xargs -n1 echo blah印刷blahすることに注意してくださいxargs
Doron Behar

11

man xargsと言う--no-run-if-empty


1
OSXを使用している場合は、そうではありません。edk750は、理由が気になる場合、コメントでこれを説明しています。
jasonleonhard 2017年

9

に関しては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でコマンドを実行している場合は、ファイルがシェルによって解釈される可能性のある特殊文字で構成されていたり、ファイルの周囲/に空白文字が含まれていると想定したりするため、結果がひどい場合があります。

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

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

1
申し訳ありませんが、while IFS= read -r -d '' file有効な方法がわかりません。説明できますか?
Adrian

2

OSXの場合:引数をxargs処理するためのBashの再実装。-rたとえば、$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 $@

2

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

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.