間違い探し
まずは hello
という内容のファイル a.txt
を作成しておく。
% echo hello > a.txt % cat a.txt hello
ここで、ファイル名 a.txt
をパイプで与えて、結果的に cat a.txt
を実行するようにしたいとする。
以下のようにコマンドを実行した。
% echo a.txt | cat a.txt
あれれ、a.txt
の中身の hello
じゃなくて、単に文字列 a.txt
が表示されてしまったぞ。
パイプの左側のコマンドの標準出力を、パイプの右側のコマンドに標準入力として与えているはずだが、なぜ。
答え合わせ
パイプで左から右に渡している「もの」は、標準出力を「箱(ファイルのようなもの?)」に入れたものになっている。
つまり、出力である「文字列」を「箱」に入れて、「箱」ごとパイプの右へ渡している感じだろうか。
一方、普段 cat a.txt
などのようにしてコマンドラインで与えている引数は、「文字列の入った箱(ファイル)」ではなく、単なる「文字列」である。
パイプの左側の標準出力を「文字列」としてそのまま右側に渡したいのであれば、それを「箱」から出してあげなければならない。
そして、それをしてくれるコマンドが xargs
である。
つまり、下記のように修正すれば良い。
% echo a.txt | xargs cat hello
こうすると、ちゃんと a.txt
の中身を cat
コマンドで表示できた。
具体例と説明?
と言われても、いまいちわからん。結局何が違うのか。
xargs
があるのとないので、出力がどう違うか、いくつか見てみる。
まず準備。
% echo hello > a.txt % ls a.txt % cat a.txt hello
いくつかサンプル。
% echo a.txt | cat a.txt % echo a.txt | xargs cat hello % echo a.txt | echo % echo a.txt | xargs echo a.txt % cat a.txt | cat hello % cat a.txt | xargs cat cat: hello: No such file or directory % cat a.txt | echo % cat a.txt | xargs echo hello
echo a.txt | cat -> a.txt
まず echo a.txt | cat
では、a.txt
という「文字列が入った箱」が引数として cat
に渡される。
これは、a.txt
という内容のファイルを cat
に渡しているのと同じ意味である。
つまり、 a.txt
が出力されることになる。
echo a.txt | xargs cat -> hello
echo a.txt | xargs cat
では、a.txt
という「文字列」が引数として cat
に渡される。
これは、cat
が引数として与えられた a.txt
という名前のファイルの中身を表示するので、hello
が表示される。
echo a.txt | echo -> ""
echo a.txt | echo
を実行すると何も表示されない。
これは、a.txt
という文字列が入った箱を echo
に渡していることになっている。
echo
は引数の「文字列」を表示するコマンドなので、「箱」を渡しても何も起きない。つまり、何も表示されない。
echo a.txt | xargs echo -> a.txt
echo a.txt | xargs echo
では a.txt
が表示される。
これは、「箱」から出した a.txt
という「文字列」が echo
に渡されるので、a.txt
が表示される。
cat a.txt | cat -> hello
cat a.txt | cat
を実行すると、a.txt
の中身である hello
が表示される。
パイプの左側の cat a.txt
で hello
が出力されるが、それをまた「箱」に入れ直してからパイプの右側の cat
に渡す。
なので、右側で再び「箱」から中身を取り出して hello
が表示される。
cat a.txt | xargs cat -> ERROR
cat a.txt | xargs cat
では、エラーが表示される。
hello
を「箱」に入っていない「文字列」としてパイプの右側の cat
に渡すので、「hello
というファイルなんてないぞ」と怒られる。
cat a.txt | echo -> ""
cat a.txt | echo
を実行しても何も表示されない。
これも、パイプの右側の echo
に「文字列」ではなく「箱」を渡しちゃってるので、表示するものがないためである。
cat a.txt | xargs echo -> hello
cat a.txt | xargs echo
を実行すると hello
が表示される。
パイプの左側の出力である hello
を「箱」に入れずに「文字列」として右側の echo
に渡しているので、hello
は表示される。
おわりに
私もよくわからなくて困っていたら、xargs
を付ければ良いとTwitterで教えてもらった。
なぜ xargs
が必要なのかはわかったが、パイプのデフォルトで xargs
を付けてくれればいいのにと思っている。
なぜパイプを通すときに、標準出力の「文字列」を「箱(ファイル? 配列?)」に入れる必要があるのだろうか。
あと便宜上「箱」と書いてきた「もの」の名前を知っている人がいたら教えて欲しいです。
man xargs
のマニュアルとかでは「string」と「argument」という言葉が出ていたが、「string」はこの記事でいう「文字列を箱に入れたもの」に対応していて、データ構造的に配列っぽいものだと思うのだが、文字列と呼んでいいのかわからない。
そもそも「ファイル」が一体何者なのか知らない。
まあそんな感じですた。