ksaitoの日記

日々試したことの覚え書き

xargs

シェルで前処理した結果のファイル一覧に対してコマンドを実行したい場合、``でコマンドを囲むのが便利です。
例えば、findコマンドで特定の条件のファイルが何かをfileコマンドで調べる場合、下記のように書くことができます。

$ find . -type f
./c
./b
./a
$ file `find . -type f`
./c: empty
./b: empty
./a: empty
$ 

例えば、特定の関数を使っているファイルを全て見つけたい場合、findの結果とgrepを組み合わせます。

$ find . -type f
./aaa
./c
./b
./a
$ grep -l aaa `find . -type f`
./aaa
./b
$ 

このファイル一覧をviやemacsで開くとすると``は、使えません。

$ vi `grep -l aaa `find . -type f``
^C
$

これは、``が入れ子にできないからです。
では、こんなときにはxargsコマンドが使えます。
xargsは、標準入力から読み込んだテキストを引数に指定されたコマンドの引数に展開してくれます。

$ grep -l aaa `find . -type f` | xargs echo
./aaa ./b
$

xargsのコマンドをviに置き換えると...

$ grep -l aaa `find . -type f` | xargs vi
Vi: Warning: Input is not from a terminal
$ 

とエラーがでて入力を受け付けなくなります。
エディタは、ユーザの入力を受け付けるためパイプで入力をつないでしまうと、キーボード入力ができなくなってしまうということです。(たぶん...)

そこで、shとシェル変数の$@、/dev/ttyを組み合わせることで特定のファイル名に特定の文字が含まれているファイルだけをエディタで開くことができます。

$ grep -l aaa `find . -type f` | xargs sh -c 'vi "$@" < /dev/tty' -
2 files to edit
$ 

今回使った例では、findコマンドのexecオプションを使ってgrepを実行すれば、xargsを使わなくてもできてしまいます。
実際に使ったのは、grepで検索する文字列が、Shift JISの日本語コメントだったケースで下記のようにして探しました。

$ grep -l `nkf -s 探したい日本語文字列` `find . -name *.[ch]` | xargs sh -c 'emacs -nw "$@" < /dev/tty' -
$