web-dev-qa-db-ja.com

stdin入力が空の場合、xargsコマンドを無視する方法は?

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

ls /mydir/*.txt | xargs chown root

意図は、mydir内のすべてのテキストファイルの所有者をルートに変更することです

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

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

158
HyderA

GNU xargsの場合、-r または --no-run-if-emptyオプション:

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

260
Sven Marnach

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
12
arielCo

man xargs--no-run-if-empty

11
thiton

xargsに関しては、提案どおり-rを使用できますが、BSD xargsではサポートされていません。

回避策として、次のような追加の一時ファイルを渡すことができます。

find /mydir -type f -name "*.txt" -print0 | xargs -0 chown root $(mktemp)

または、その標準エラーを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 command の出力を解析すべきではありません(参照: なぜlsの出力を解析すべきでないのか )。特に、コマンドをルートで実行している場合、ファイルはシェルによって解釈される可能性のある特殊文字で構成されたり、/の周りにスペース文字があるファイルを想像できるため、結果がひどくなる可能性があります。

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

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

OSX:-r引数を処理するxargsのBash再実装。 $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
rcomblen

これは、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"引数をサポートしていません。

0
Mirko Steiner