Linux の 101 試験対策: ストリーム、パイプ、リダイレクト

Linux のリダイレクトやパイプの操作を理解する

ストリーム、パイプと聞いて、Linux® のエキスパートはまるで配管工のようだと思っているのなら、これを機会にストリームとパイプについて学んでください。今回の記事ではストリームをリダイレクトする方法と複数の出力に分割する方法、さらにストリームをコマンドの引数として使えるようにする方法も説明します。この記事の内容は、Linux のシステム管理者として認定するための LPI® 101 試験に備えるためにも、自ら活用するために学ぶ上でも役に立ちます。

Ian Shields, Senior Programmer, IBM

Ian ShieldsIan Shields は、developerWorks Linux ゾーンの様々な Linux プロジェクトに関わっています。彼はノースキャロライナ州 Research Triangle Park にある IBM のシニア・プログラマーです。1973年にオーストラリアのキャンベラでシステム・エンジニアとして IBM に入社して以来、カナダのモントリオールやノースキャロライナ州 Research Triangle Park で、コミュニケーション・システムやパーベイシブ・コンピューティングに携わってきました。彼はいくつかの特許を保持しています。Australian National University にて純粋数学および哲学で学位を取得し、また North Carolina State University にてコンピューター・サイエンスで修士号と博士号を取得しています。Ian について詳しく知るには、My developerWorks で彼のプロフィールを見てください。


developerWorks 貢献著者レベル

2009年 10月 14日

この連載について

この連載は Linux システム管理タスクの学習に役立つだけでなく、LPIC-1 (Linux Professional Institute Certification レベル 1) 試験に備えるための教材にもなります。

連載の各記事についての説明とリンクについては、連載のロードマップを参照してください。現在進行中のこのロードマップは、LPIC-1 試験の最新の目標 (2009年4月) を反映しています。完成した記事はその都度ロードマップに追加されていきますが、当面は developerWorks の LPI 認定試験対策チュートリアルで同様の教材の以前のバージョンを調べてください。これらのバージョンは、2009年4月より前の LPIC-1 目標に対応しています。

前提条件

この連載の記事を最大限に活用するには、Linux の基礎知識と、記事に記載されたコマンドを演習できる実際の Linux システムが必要です。プログラムのバージョンによって出力のフォーマットに違いが出てくる場合もあるため、コマンドの実行結果は必ずしもここに記載するリストや図とまったく同じであるとは限りません。

概要

この記事では、標準入出力ストリームをリダイレクトするための基本的な Linux の手法を説明します。この記事で説明する内容は以下のとおりです。

  • 標準入出力ストリーム (標準入力、標準出力、標準エラー) をリダイレクトする方法
  • あるコマンドの出力を別のコマンドの入力にパイプする方法
  • 出力を標準出力とファイルの両方に送る方法
  • コマンドの出力を別のコマンドの引数として使用する方法

この記事は、Linux Professional Institute の Junior Level Administration (LPIC-1) 101 試験の主題 103 の 103.4 の試験対策となります。この目標の重要度は 4 です。


サンプルのセットアップ

この記事では、記事「Linux の 101 試験対策: テキスト・ストリームとフィルター」で作成したファイルを用いてコマンドの実践演習を行いますが、まだその記事の内容を読んでいなかったり、読んだけれども作成したファイルを保存していなかったりしたとしても問題ありません。まずは、ホーム・ディレクトリーに lpi103-4 という名前の新しいサブディレクトリーを作成し、この記事で必要になるファイルを作成するところから始めましょう。それにはまず、ホーム・ディレクトリーをカレント・ディレクトリーとしてテキスト・ウィンドウを開いてください。次にリスト 1 の内容をテキスト・ウィンドウにコピーして、これらのコマンドを実行すると、これから使用する lpi103-4 サブディレクトリーとファイルが作成されます。

リスト 1. サンプル・ファイルの作成
mkdir -p lpi103-4 && cd lpi103-4 && {
echo -e "1 apple\n2 pear\n3 banana" > text1
echo -e "9\tplum\n3\tbanana\n10\tapple" > text2
echo "This is a sentence. " !#:* !#:1->text3
split -l 2 text1
split -b 17 text2 y; }

ウィンドウにはリスト 2 のような内容が表示され、カレント・ディレクトリーは新しく作成された lpi103-4 ディレクトリーになっているはずです。

リスト 2. サンプル・ファイルの作成 – 出力

リスティングを見るにはここをクリック

リスト 2. サンプル・ファイルの作成 – 出力

[ian@echidna ~]$ mkdir -p lpi103-4 && cd lpi103-4 && {
> echo -e "1 apple\n2 pear\n3 banana" > text1
> echo -e "9\tplum\n3\tbanana\n10\tapple" > text2
> echo "This is a sentence. " !#:* !#:1->text3echo "This is a sentence. " "This is a sentence. " "This is a sentence. ">text3
> split -l 2 text1
> split -b 17 text2 y; }
[ian@echidna lpi103-4]$

標準入出力のリダイレクト

bash などの Linux シェルは、入力および出力を文字のシーケンス、つまりストリームとして受け渡しします。それぞれの文字は、その前後の文字とは独立しています。これらの文字は構造化されたレコードや固定サイズのブロックには編成されません。ストリームにアクセスするにはファイル入出力の手法を使用します。この手法は、実際の文字を送り込む元となるデバイス、または送り込む先のデバイスがファイル、キーボード、ディスプレイ上のウィンドウ、あるいはその他の入出力デバイスのどれであろうと関係なく、共通して使用するアクセス方法です。Linux シェルが使用する標準入出力ストリームは 3 つあり、そのそれぞれには周知のファイル記述子が関連付けられています。

  1. stdout。コマンドからの出力を表示する標準出力ストリームです。このストリームのファイル記述子は 1 です。
  2. stderr。コマンドからのエラー出力を表示する標準エラー・ストリームです。このストリームのファイル記述子は 2 です。
  3. stdin。コマンドに入力を提供する標準入力ストリームです。このストリームのファイル記述子は 0 です。

入力ストリームがプログラムに提供する入力は、通常は端末のキー・ストロークによるものです。出力ストリームは端末にテキスト文字を出力するのが通常です。当初、端末は ASCII タイプライターやディスプレイ端末でしたが、今ではグラフィカル・デスクトップ上のテキスト・ウィンドウであることの方が多くなっています。

記事「Linux の 101 試験対策: テキスト・ストリームとフィルター」を読んでいれば、今回の記事で使用する一部の教材はすでにお馴染みのはずです。

出力をリダイレクトする方法

出力をファイルにリダイレクトするには、2 つの方法があります。

n>
ファイル記述子 n の出力をファイルにリダイレクトします。この操作を行うには、ファイルに対する書き込み権限が必要です。ファイルが存在しなければ、該当するファイルが新規に作成されます。ファイルが存在する場合、ファイルの既存の内容が上書きされますが、通常、警告は出されません。
n>>
同じくファイル記述子 n の出力をファイルにリダイレクトします。この場合にも、ファイルに対する書き込み権限が必要です。ファイルが存在しなければ、該当するファイルが新規に作成されます。存在する場合には、出力が既存のファイルの最後に追加されます。

n> や n>> の n は、ファイル記述子であり、省略された場合はデフォルトで標準出力が使用されます。リスト 3 に、リダイレクトを使用して、ls コマンドによる標準出力と標準エラーを分ける方法を示します。使用するファイルは、前のセットアップ手順でサンプル lpi103-4 ディレクトリーに作成したファイルです。このリストには、出力を既存のファイルに追加する方法も示しています。

リスト 3. 出力のリダイレクト
[ian@echidna lpi103-4]$ ls x* z*
ls: cannot access z*: No such file or directory
xaa  xab
[ian@echidna lpi103-4]$ ls x* z* >stdout.txt 2>stderr.txt
[ian@echidna lpi103-4]$ ls w* y*
ls: cannot access w*: No such file or directory
yaa  yab
[ian@echidna lpi103-4]$ ls w* y* >>stdout.txt 2>>stderr.txt
[ian@echidna lpi103-4]$ cat stdout.txt
xaa
xab
yaa
yab
[ian@echidna lpi103-4]$ cat stderr.txt
ls: cannot access z*: No such file or directory
ls: cannot access w*: No such file or directory

前述のとおり、n> を使用して出力をリダイレクトすると既存のファイルが上書きされますが、この振る舞いは組み込みコマンド setnoclobber オプションを使用して制御することができます。このオプションが設定されている場合、リダイレクトによる既存のファイルの上書きが禁止されますが、n>| を使用することで、上書き禁止の設定を一時的に無効にすることができます (リスト 4 を参照)。

リスト 4. noclobber を設定して出力をリダイレクトする例
[ian@echidna lpi103-4]$ set -o noclobber
[ian@echidna lpi103-4]$ ls x* z* >stdout.txt 2>stderr.txt
-bash: stdout.txt: cannot overwrite existing file
[ian@echidna lpi103-4]$ ls x* z* >|stdout.txt 2>|stderr.txt
[ian@echidna lpi103-4]$ cat stdout.txt
xaa
xab
[ian@echidna lpi103-4]$ cat stderr.txt
ls: cannot access z*: No such file or directory
[ian@echidna lpi103-4]$ set +o noclobber #restore original noclobber setting

標準出力と標準エラーを 1 つのファイルにリダイレクトしたいという場合もあります。これは自動プロセスやバックグラウンド・ジョブではよくあることで、こうすることによって後で出力を調べられるようにするというわけです。標準出力と標準エラーの両方を同じ場所にリダイレクトするには、&> または &>> を使用します。あるいは m>&n または m>>&n を使ってファイル記述子 n をリダイレクトしてから、ファイル記述子 m を同じ場所にリダイレクトするという方法もあります。この場合、出力をリダイレクトする順序が重要です。以下の例を見てください。
command 2>&1 >output.txt
これは以下のコマンドとは違います。
command >output.txt 2>&1
最初の例では、stderr が stdout の現行の場所にリダイレクトされ、それから stdout が output.txt にリダイレクトされます。しかし後者のリダイレクトが適用されるのは stdout だけで、stderr には適用されません。一方、2 番目の例では、stderr がリダイレクトされるのは stdout の現行の場所であり、すなわち output.txt です。リスト 5 に、これらのリダイレクトの違いを示します。注目する点として、最後のコマンドでは標準エラーの後に標準出力がリダイレクトされているため、標準エラーの出力は相変わらずターミナル・ウィンドウに送られます。

リスト 5. 2 つのストリームを 1 つのファイルにリダイレクトする例
[ian@echidna lpi103-4]$ ls x* z* &>output.txt
[ian@echidna lpi103-4]$ cat output.txt
ls: cannot access z*: No such file or directory
xaa
xab
[ian@echidna lpi103-4]$ ls x* z* >output.txt 2>&1
[ian@echidna lpi103-4]$ cat output.txt
ls: cannot access z*: No such file or directory
xaa
xab
[ian@echidna lpi103-4]$ ls x* z* 2>&1 >output.txt # stderr does not go to output.txt
ls: cannot access z*: No such file or directory
[ian@echidna lpi103-4]$ cat output.txt
xaa
xab

あるいは、標準出力も標準エラーもすべて無視したいという場合もあります。それには、該当するストリームを空のファイル、/dev/null にリダイレクトしてください。リスト 6 に、ls コマンドからのエラー出力を無視する方法を説明するとともに、cat コマンドを使用して /dev/null が実際に空であることを示します。

リスト 6. /dev/null を使用して出力を無視する例
[ian@echidna lpi103-4]$ ls x* z* 2>/dev/null
xaa  xab
[ian@echidna lpi103-4]$ cat /dev/null

入力をリダイレクトする方法

stdout および stderr ストリームをリダイレクトできるのと同じく、< 演算子を使用すれば、ファイルからの stdin をリダイレクトすることも可能です。記事「Linux の 101 試験対策: テキスト・ストリームとフィルター」の「sort と uniq」セクションで説明したので覚えているかもしれませんが、そこでは tr コマンドを使用してサンプル・ファイル text1 のスペースをタブに置き換えました。そのときの例では、tr コマンドの標準入力を作成するために cat コマンドからの出力を使用しましたが、cat を不必要に呼び出す代わりに、入力のリダイレクトを使ってスペースをタブに変換することができます (リスト 7 を参照)。

リスト 7. 入力のリダイレクト
[ian@echidna lpi103-4]$ tr ' ' '\t'<text1
1       apple
2       pear
3       banana

bash を含め、シェルには入力をリダイレクトする別の形として、ヒアドキュメントという概念もあります。この概念では << と併せて、入力の終わりを示すマーカーすなわち標識として END などの単語を使用します。リスト 8 に一例を記載します。

リスト 8. ヒアドキュメントを使用した入力のリダイレクト
[ian@echidna lpi103-4]$ sort -k2 <<END
> 1 apple
> 2 pear
> 3 banana
> END
1 apple
3 banana
2 pear

sort -k2 と入力し、データを入力してから Ctrl-d を押して入力の終わりを知らせるだけでは駄目なのかと思うかもしれませんが、簡単に答えればその方法でも問題ありません。しかし、それではヒアドキュメントについて理解することはできません。実のところ、ヒアドキュメントはむしろシェル・スクリプトで盛んに使用される概念です (スクリプトには、ヒアドキュメント以外に、スクリプトのどの行を入力として扱うべきかを通知する手段がありません)。またシェル・スクリプトはタブを多用して、読みやすくするためのインデントを付けることから、ヒアドキュメントにはさらにもう 1 つの仕掛けがあります。それは、<< の代わりに <<- を使用すると、各行頭のタブが取り除かれることです。

リスト 9 では、コマンド置換を使用して専用のタブ文字を作成した後、ごく小さなシェル・スクリプトを作成しています。このシェル・スクリプトに含まれるのは、それぞれにヒアドキュメントから読み込えむ 2 つの cat コマンドです。端末から読み取っているヒアドキュメントには、END という単語を標識として使用していることに注意してください。これと同じ単語をスクリプトのなかで使用したとすると、入力は不完全なまま途中で終わってしまいます。そのため、スクリプトのなかでは EOF を使用しています。スクリプトを作成した後は、. (ドット) コマンドを使用してソースを指定します。つまり、スクリプトを現行のシェル・コンテキストのなかで実行するということです。

リスト 9. ヒアドキュメントを使用した入力のリダイレクト
[ian@echidna lpi103-4]$ ht=$(echo -en "\t")
[ian@echidna lpi103-4]$ cat<<END>ex-here.sh
> cat <<-EOF
> apple
> EOF
> ${ht}cat <<-EOF
> ${ht}pear
> ${ht}EOF
> END
[ian@echidna lpi103-4]$ cat ex-here.sh
cat <<-EOF
apple
EOF
        cat <<-EOF
        pear
        EOF
[ian@echidna lpi103-4]$ . ex-here.sh
apple
pear

コマンド置換およびスクリプトの作成については、連載の今後の記事で詳しく説明する予定です。連載の各記事についての説明とリンクについては、連載のロードマップを参照してください。


パイプラインの作成

記事「Linux の 101 試験対策: テキスト・ストリームとフィルター」では、テキストのフィルタリングとは、テキストの入力ストリームを受け取り、そのテキストに対して何らかの変換を行ってから出力ストリームに送るプロセスのことだと説明しました。このようなフィルタリングを行う際に最もよく使用される方法が、コマンドのパイプラインを作成することです。パイプラインでは、あるコマンドの出力が次のコマンドの入力としてパイプされるかリダイレクトされます。このようなパイプラインはテキスト・ストリームでしか使用できないわけではありませんが、それでもやはり、テキスト・ストリームで最もよく使われています。

stdout を stdin にパイプする方法

最初のコマンドの stdout を次のコマンドの stdin に送るには、この 2 つのコマンドを | (パイプ) 演算子でつなげます。さらにコマンドを追加してパイプ演算子でつなげていくことにより、パイプラインは長くなっていきます。パイプラインを構成するいずれのコマンドにしても、オプションまたは引数を使用することができます。入力をファイルからではなく stdin から受け取る場合、多くのコマンドではファイル名の代わりにハイフン (-) を引数として使用します。使用するコマンドがこれに該当するかどうかは、それぞれのコマンドの man ページで確認してください。1 つのタスクを達成するために、それぞれに限られた機能を持つコマンドをいくつも接続してパイプラインを構成するという方法は、Linux および UNIX® では一般的になっています。リスト 10 のパイプラインの例では、command2command3 の両方にパラメーターがありますが、command3 では - パラメーターを単独で使用して、stdin からの入力であることを示しています。

リスト 10. 出力を複数のコマンドにパイプする例
command1 | command2 paramater1 | command3 parameter1 - parameter2 | command4

ここで注意しなければならない点として、パイプラインは stdout のみを stdin にパイプします。2| を使って stderr だけをパイプすることは、少なくとも今まで学んだツールでは不可能ですが、stderr が stdout にリダイレクトされていれば、この両方のストリームがパイプされることになります。リスト 11 に示すのは、ありそうもない ls コマンドですが、このコマンドはアルファベット順ではない 4 つのワイルドカード引数を使用し、次にパイプを使って標準出力とエラー出力を結合してソートします。

リスト 11. 2 つの出力ストリームをパイプする例
[ian@echidna lpi103-4]$ ls y* x* z* u* q*
ls: cannot access z*: No such file or directory
ls: cannot access u*: No such file or directory
ls: cannot access q*: No such file or directory
xaa  xab  yaa  yab
[ian@echidna lpi103-4]$ ls y* x* z* u* q*  2>&1 |sort
ls: cannot access q*: No such file or directory
ls: cannot access u*: No such file or directory
ls: cannot access z*: No such file or directory
xaa
xab
yaa
yab

Linux および UNIX システムでのパイプの利点は、他のよく使われているオペレーティング・システムとは異なり、パイプには中間ファイルが一切関与しないことです。つまり、最初のコマンドの stdout がファイルに書き込まれた上で、2 番目のコマンドがそれを読み取るということはありません。記事「Linux の 101 試験対策: ファイルとディレクトリーの管理」では、tar コマンドを使用して 1 つのステップでファイルをアーカイブおよび圧縮する方法を説明しましたが、tar コマンドに -z (gzipの場合) または -j (bzip2 の場合) による圧縮オプションがないような UNIX システムで作業をしているとしても問題はありません。その場合には、以下のようにパイプラインを使用して対処することができます。

bunzip2 -c somefile.tar.bz2 | tar -xvf -

このコマンドによって、圧縮が行われます。

stdout ではなくファイルでパイプラインを開始する方法

上記のパイプラインでは、まず出力を生成する何らかのコマンドを使用してから、その出力をパイプラインの各ステージでつなげていますが、すでに存在するデータのファイルからパイプラインを開始しなければならない場合はどうすればよいのでしょうか。多くのコマンドは stdin またはファイルを入力として取るため、これらのコマンドについては問題ありませんが、stdin やファイルを入力として取れないコマンドの場合は、stdin からの入力を必要とするフィルターを使用することで (例えば cat コマンドを使用してファイルを stdout にコピーするなど)、上手くいくはずです。これよりも便利な方法は、最初のコマンドに入力リダイレクトを使用し、そのコマンドの出力をパイプラインの残りの部分につなげるという方法です。最初のコマンドの stdin を処理対象のファイルにリダイレクトするには、< 演算子を使うだけで済みます。


引数としての出力の使用

パイプラインについての前のセクションでは、あるコマンドの出力を取り、それを別のコマンドの入力として使用する方法を学びました。しかし例えば、コマンドの出力やファイルの内容を入力としてではなく、コマンドの引数として使用したい場合にはどうすればよいでしょうか。パイプラインでは、このような操作に対処することができません。この場合の一般的な方法としては、以下の 3 つが使用されます。

  1. xargs コマンド
  2. -exec オプションを使用した find コマンド
  3. コマンド置換

上記に挙げた 3 つのうち、ここでは最初の 2 つの方法を説明します。コマンド置換に関しては、リスト 9 にその一例があり、専用のタブ文字を作成したときにコマンド置換を使用しました。コマンド置換はコマンドラインでも使用されるものの、それよりも頻繁に使用されるのはスクリプト内です。スクリプトおよびスクリプト内でのコマンド置換については、連載の今後の記事で詳しく説明します。連載の各記事についての説明とリンクについては、連載のロードマップを参照してください。

xargs コマンドを使用する方法

xargs コマンドは標準入力を読み取り、その入力をパラメーターとしてコマンドを組み立て、実行します。コマンドが指定されていない場合は、echo コマンドが使用されます。リスト 12 は、サンプル・ファイル text1 を使用した基本的な例です。このファイルには、番号と単語で構成される行が 3 行含まれています。

リスト 12. xargs の使用
[ian@echidna lpi103-4]$ cat text1
1 apple
2 pear
3 banana
[ian@echidna lpi103-4]$ xargs<text1
1 apple 2 pear 3 banana

上記の xargs からの出力が 1 行だけになっているのはなぜでしょう。デフォルトでは、xargs は入力を空白で分割し、分割後のそれぞれのトークンがパラメーターとなります。けれども xargs がコマンドを組み立てる場合には、一度にできるだけ多くのパラメーターを渡します。この振る舞いは、-n または --max-args パラメーターで変更することができます。リスト 13 では、この両方のパラメーターの使い方を示すとともに、xargs コマンド内で明示的な echo 呼び出しを行っています。

リスト 13. xargsecho の使用
[ian@echidna lpi103-4]$ xargs<text1 echo "args >"
args > 1 apple 2 pear 3 banana
[ian@echidna lpi103-4]$ xargs --max-args 3 <text1 echo "args >"
args > 1 apple 2
args > pear 3 banana
[ian@echidna lpi103-4]$ xargs -n 1 <text1 echo "args >"
args > 1
args > apple
args > 2
args > pear
args > 3
args > banana

入力に含まれている空白が引用符または二重引用符で囲われている場合、あるいはバックスラッシュでエスケープされている場合には、xargs はこれらの箇所では入力を分割しません。リスト 14 に、これに該当する例を示します。

リスト 14. 引用符を使用した xargs の例
[ian@echidna lpi103-4]$ echo '"4 plum"' | cat text1 -
1 apple
2 pear
3 banana
"4 plum"
[ian@echidna lpi103-4]$ echo '"4 plum"' | cat text1 - | xargs -n 1
1
apple
2
pear
3
banana
4 plum

ここまでは、引数はすべてコマンドの終わりに追加されていましたが、入力を引数として使用し、それに続けて別の引数を使用しなければならない場合もあります。その場合には -I オプションで置換文字列を指定します。xargs に実行させるコマンド内では、置換文字列がある場所はすべて引数に置き換えられます。この方法をとると、各コマンドには 1 つの引数しか渡されません。しかし、引数は単一のトークンだけから作成されるのではなく、入力行全体から作成されます。そこで、-L オプションを使用することで、デフォルトの空白で区切られた個々のトークンではなく、xargs に行を引数として処理させることもできます。-I オプションを使用するということは、-L 1 を指定することと同じです。リスト 15 に、-I-L 両方の使用例を記載します。

リスト 15. 入力行を使用した xargs の例
[ian@echidna lpi103-4]$ xargs -I XYZ echo "START XYZ REPEAT XYZ END" <text1
START 1 apple REPEAT 1 apple END
START 2 pear REPEAT 2 pear END
START 3 banana REPEAT 3 banana END
[ian@echidna lpi103-4]$ xargs -IX echo "<X><X>" <text2
<9      plum><9 plum>
<3      banana><3       banana>
<10     apple><10       apple>
[ian@echidna lpi103-4]$ cat text1 text2 | xargs -L2
1 apple 2 pear
3 banana 9 plum
3 banana 10 apple

上記の例では説明のために単純なテキスト・ファイルを使用しましたが、このような入力で xargs を使用することはめったにありません。通常は、lsfind、または grep などのコマンドから生成された大きなファイルのリストを扱うことになります。リスト 16 に、xargs を使って grep などのコマンドに直接ディレクトリー・リストを渡す例を示します。

リスト 16. ファイル・リストを使用した xargs の例
[ian@echidna lpi103-4]$ ls |xargs grep "1"
text1:1 apple
text2:10        apple
xaa:1 apple
yaa:1

例えば上記の例で、1 つまたは複数のファイル名にスペースが含まれている場合を考えてみてください。リスト 16 のコマンドをそのまま使用したのでは、エラーが発生することになります。実際には、ファイルのリストは、ls ではなくカスタム・スクリプトまたはコマンドから提供される場合があります。あるいは、ファイルのリストを他のパイプライン・ステージを通過させて、さらにフィルタリングしたいという場合もあるので、ここでは上記の構文の代わりに単に grep "1" * を使用できるという事実は無視することにします。

ls コマンドの場合、--quoting-style オプションを使用して問題のあるファイル名を強制的に引用符で囲むか、またはエスケープさせるという方法がありますが、xargs-0 オプションを使用できる場合は、この方法のほうが有効なソリューションです。このオプションは入力引数をヌル文字 (\0) で区切ります。ls には出力されるファイル名の後にヌル文字を付加するオプションはありませんが、他の多くのコマンドにはこのオプションがあります。

リスト 17 では、まず text1 を “text 1” にコピーした上で、空白が含まれるファイル名のリストを xargs で使用する何通りかの方法を示します。何通りかの方法を示したのは、xargs を使いこなすのは難しいからであり、例を見ることで xargs コマンドの概念を理解してもらえるようにするためです。具体的に言うと、改行文字をヌルに変換する最後の例は、ファイル名にすでに改行文字が含まれている場合には上手くいきません。これよりも確実なソリューションとして、この後のセクションでは find コマンドを使って適切にヌルで区切られた出力を生成する方法を説明します。

リスト 17. ファイル名に空白が含まれる場合の xargs の使用例
[ian@echidna lpi103-4]$ cp text1 "text 1"
[ian@echidna lpi103-4]$ ls *1 |xargs grep "1" # error
text1:1 apple
grep: text: No such file or directory
grep: 1: No such file or directory
[ian@echidna lpi103-4]$ ls --quoting-style escape *1
text1  text\ 1
[ian@echidna lpi103-4]$ ls --quoting-style shell *1
text1  'text 1'
[ian@echidna lpi103-4]$ ls --quoting-style shell *1 |xargs grep "1"
text1:1 apple
text 1:1 apple
[ian@echidna lpi103-4]$ # Illustrate -0 option of xargs
[ian@echidna lpi103-4]$ ls *1 | tr '\n' '\0' |xargs -0 grep "1"
text1:1 apple
text 1:1 apple

xargs コマンドが組み立てるコマンドは、無制限に長くできるというわけではありません。Linux カーネル 2.26.3 までは、コマンドの最大サイズは制限されていました。例えば rm somepath/* などのコマンドは、長い名前が付けられた数多くのファイルが含まれるディレクトリーに対しては、引数リストが長すぎるという理由で失敗します。古い Linux システムや、あるいは今でも制限が設けられている UNIX システムでは、このような事態に対処できるように xargs の使い方を知っておくと役に立ちます。

--show-limits オプションを使用すると、xargs のデフォルト制限を表示することができます。そして出力コマンドのサイズを特定の最大文字数に制限するには、-s オプションを使用します。ここで説明していないその他のオプションについては、man ページを参照してください。

-exec オプションまたは xargs と併せて find コマンドを使用する方法

記事「Linux の 101 試験対策: ファイルとディレクトリーの管理」では、find コマンドを使用して、名前、変更時刻、サイズやその他の属性を基準にファイルを検索する方法を説明しました。一連のファイルが見つかった後は大抵、検出されたファイルで何らかの操作 (ファイルの削除、別の場所へのファイルのコピー、ファイル名の変更など) を実行することになります。そこでこれから説明するのが、find-exec オプションです。このオプションは、find の出力を xargs にパイプするのと同様の機能を果たします。

リスト 18. find-exec の使用
[ian@echidna lpi103-4]$ find text[12] -exec cat text3 {} \;
This is a sentence.  This is a sentence.  This is a sentence.
1 apple
2 pear
3 banana
This is a sentence.  This is a sentence.  This is a sentence.
9       plum
3       banana
10      apple

リスト 18 の例は、これまでに xargs の使用について説明した内容と比べると、いくつかの違いがあります。

  1. コマンド内でのファイル名の位置を示す {} を含める必要があります。末尾に自動的に追加されることはありません。
  2. コマンドはセミコロンで終了し、エスケープする必要があります。エスケープ文字には \;、';'、または “;” が有効です。
  3. コマンドはそれぞれの入力ファイルに対し、1 回実行されます。

find text[12] |xargs cat text3 を実行して、違いを確かめてみてください。

ファイル名に空白が含まれている場合の話に戻りましょう。リスト 19 では、lsxargs を使用する代わりに、-exec を指定した find を試してみます。

リスト 19. ファイル名に空白が含まれる場合の -exec を指定した find の使用例
[ian@echidna lpi103-4]$ find . -name "*1" -exec grep "1" {} \;
1 apple
1 apple

今のところ問題はありませんが、何かが足りないとは思いませんか?それは、どのファイルに、grep で検出された行が含まれていたのかが示されていないことです。このようにファイル名が欠落している理由は、find はそれぞれのファイルに対して 1 回 grep を実行しますが、grep には、1 つのファイル名だけが指定されている場合には検出された結果がどのファイルに含まれているかを知らせる必要がないことを認識するだけの賢さがあるからです。

代わりに xargs を使用することもできますが、前に説明したとおり、ファイル名に空白が含まれる場合には問題となります。さらに、find はヌル区切り文字を使用したファイル名のリストを生成できるという点も示唆しました。これは -print0 オプションの機能と同じです。find の最近のバージョンは、セミコロンではなく + 記号で区切ることができます。この場合、xargs と同じように、find はできるだけ多くの名前を 1 回のコマンド呼び出しで渡すことになります。言うまでもなく、この場合に {} を使用できるのは 1 度だけですが、これはコマンドの最後のパラメーターでなくてはなりません。リスト 20 に、この両方の方法を記載します。

リスト 20. ファイル名に空白が含まれる場合の findxargs の使用例
[ian@echidna lpi103-4]$ find . -name "*1" -print0 |xargs -0 grep "1"
./text 1:1 apple
./text1:1 apple
[ian@echidna lpi103-4]$ find . -name "*1" -exec grep "1" {} +
./text 1:1 apple
./text1:1 apple

一般に、いずれの方法も有効であり、どちらを選ぶかは個人の好みの問題です。しかし、パイプする対象に空白やホワイト・スペースが引用符で囲われずに含まれていたり、エスケープされずに含まれていたりすると問題が発生するため、出力を xargs にパイプする場合には find-print0 オプションを使用し、xargs で -0 オプションを使用することで xargs にはヌルで区切られた入力が渡されることを指定する必要があります。tar をはじめとするその他のコマンドも -0 オプションを使用することで、ヌルで区切られた入力をサポートするようになるので、入力リストが問題にならないことが確実でない限り、ヌルで区切られた入力をサポートするコマンドには必ず -0 オプションを使用してください。

ファイル・リストの操作に関する最後のコメントとして、ファイルの削除や名前変更などの一括操作を行う場合は、一括操作の実行前にファイル・リストを完全にテストすること、そしてコマンドも慎重にテストすることが常に賢明な手段となります。また、十分なバックアップを用意しておくことも非常に有効です。


出力の分割

このセクションでは、もう 1 つのコマンドについて簡単に説明して、記事を締めくくります。時として、後で使用できるように出力のコピーを保存する一方、出力を画面上に表示したい場合があります。それには、一方のウィンドウでコマンドの出力をファイルにリダイレクトし、それから tail -fn1 を使用して別の画面で出力を辿るという方法が考えられますが、それよりも簡単な方法となるのが、tee` コマンドを使用することです。

tee はパイプラインと併せて使用します。引数は、標準出力の 1 つ (または複数) のファイルです。-a オプションは、ファイルを上書きするのではなく、ファイルの最後に出力を追加します。パイプラインのセクションで説明したように、標準出力と標準エラーの両方を保存したい場合には、tee にパイプする前に stderr を stdout にリダイレクトする必要があります。リスト 21 は、tee を使用して出力を f1 と f2 の 2 つのファイルに保存する例です。

リスト 21. tee による stdout の分割
[ian@echidna lpi103-4]$ ls text[1-3]|tee f1 f2
text1
text2
text3
[ian@echidna lpi103-4]$ cat f1
text1
text2
text3
[ian@echidna lpi103-4]$ cat f2
text1
text2
text3

参考文献

学ぶために

製品や技術を入手するために

  • developerWorks から直接ダウンロードできる IBM 試用版ソフトウェアを使用して、Linux で次の開発プロジェクトを構築してください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Linux
ArticleID=446929
ArticleTitle=Linux の 101 試験対策: ストリーム、パイプ、リダイレクト
publish-date=10142009