103.4 レッスン 1
Certificate: |
LPIC-1 (101) |
---|---|
Version: |
5.0 |
Topic: |
103 GNUおよびUnixコマンド |
Objective: |
103.4 ストリーム、パイプ、リダイレクトを使う |
Lesson: |
1 of 2 |
はじめに
すべてのコンピュータプログラムは、同じ原則に従います。すなわち、何らかのソースデータを読み込んで、それらを加工して結果を出力することです。Linuxシェルの場合は、ローカルファイル、リモートファイル、キーボードなどのデバイスが、データソースとなります。出力は画面に表示されるのが一般的ですが、出力データをローカルファイルシステムに保存したり、リモートデバイスに送信したり、あるいはオーディオスピーカーで再生することなどもあります。
LinuxのようにUnixに触発されたオペレーティングシステムは、多種多様な入出力を提供します。整数とデータチャンネルを動的に関連付ける ファイル記述子 の概念では、プロセスはその入力/出力データストリームを、整数値で参照することができます。
標準的なLinuxプロセスは、デフォルトで3つの通信チャネル、標準入力 チャネル(stdin)、標準出力 チャネル(stdout)、および 標準エラー チャネル(stderr)が開かれています。これらのチャネルに割り当てられたファイル記述子(整数)は、stdinが 0
、stdoutが 1
、stderrが 2
です。通信チャネルには、特別なデバイス /dev/stdin
、/dev/stdout
、/dev/stderr
を介してアクセスすることもできます。
プログラミングの際には、これらの3つの標準通信チャネルを使えば、データを入出力するメディアの種類を気にしなくても、読み取りと書き込みを行うコードを記述できます。たとえば、プログラムが一群のデータを入力として必要とする場合、標準入力からデータを読み出せば、そのチャンネルに関連付けられている実体(ファイルやデバイス)がすべてのデータを提供します。同様に、プログラムが処理結果を出力するは、それを標準出力に書き込むのが一番単純です。通常のシェルセッションでは、キーボードがstdinに関連付けられ、モニター画面がstdoutとstderrの両方に関連付けられます。
Bashシェルには、プログラムのロード時に、これらの通信チャネルを再割り当てする機能が備わっています。これにより、たとえばstdoutをモニター画面からローカルファイルに切り替えて使用する事ができます。
リダイレクト
シェル環境における、ファイル記述子に対するチャネルの再割り当てを リダイレクト と呼びます。リダイレクトは、コマンドライン内の特殊文字で指定します。たとえば、プロセスの標準出力をファイルにリダイレクトするには、コマンドの最後に 大なり記号 >
を置き、リダイレクトされた出力を受け取るファイルへのパスを続けます。
$ cat /proc/cpuinfo >/tmp/cpu.txt
デフォルトでは、stdout に送信されるコンテンツのみがリダイレクトされます。詳しく述べると、Bashでは大なり記号の直前に、切り替えるファイル記述子(数値)を指定するのですが、省略された場合には標準出力をリダイレクトします。つまり、>
のみを指定することは、1>
を指定することと同じです (stdout のファイル記述子は 1
です)。
stderrのコンテンツをリダイレクトするには、代わりにリダイレクトとして 2>
を指定します。ほとんどのコマンドラインプログラムは、デバッグ情報とエラーメッセージを標準エラーチャネルに送信します。たとえば、存在しないファイルを読み取ろうとしたときに表示されるエラーメッセージをファイルにキャプチャするには、次のようにします。
$ cat /proc/cpu_info 2>/tmp/error.txt $ cat /tmp/error.txt cat: /proc/cpu_info: No such file or directory
stdoutとstderrの両方を同じターゲットにリダイレクトするには、&>
ないし >&
を使用します。大なり記号(>)とアンパサンド(&)の間に空白を置いてはいけません。空白を置くと、Bashはアンパサンドを、バックグラウンドでプロセスを実行する指示とみなして、リダイレクトは行われません。
リダイレクト先のターゲットは、書き込み可能なファイルへのパス(/tmp/cpu.txt
など)、ないしは、書き込み可能なファイル記述子であることが必要です。ターゲットにファイル記述子を指定するには、アンパサンドに続けてファイル記述子(数値)を置きます。たとえば、1>&2
はstdoutをstderrにリダイレクトします。逆に、stderrをstdoutにリダイレクトするには、2>&1
を指定します。
あまり便利とは言えませんが、stdoutをファイルにリダイレクトしておき、さらにstderrをstdoutにリダイレクトすれば、短く書くことができます。たとえば、stderrとstdoutの両方を log.txt
という名前のファイルに書き込むリダイレクトは、>log.txt 2>&1
と書くことができます。なお、stderrをstdoutにリダイレクトする主な理由は、デバッグメッセージやエラーメッセージを解析するためです。プログラムの標準出力を別のプログラムの標準入力にリダイレクトすること(パイプ)はできますが、標準エラーを別のプログラムの標準入力に直接リダイレクトすることはできません。つまり、別のプログラムのstdinに繋げられるように、stderrに送られるメッセージを、stdoutにリダイレクトする必要があります。
コマンドの出力を破棄したければ、その内容を特別なファイル /dev/null
にリダイレクトします。たとえば >log.txt 2>/dev/null
は、stdoutの出力をファイル log.txt
に保存し、stderrを破棄します。ファイル /dev/null
はどのユーザーでも書き込み可能ですが、書き込んだ内容はどこにも保存されずに破棄されます。
指定されたターゲットが書き込み可能でなく(パスがディレクトリまたは読み取り専用ファイルを指している場合)、変更できない場合はエラーメッセージが表示されます。ターゲットが書き込み可能であれば、出力リダイレクトは、確認なしで既存のターゲットを上書きします。Bashのオプション noclobber
を有効とすれば、既存のファイルの上書きを禁止できます。このオプションを現在のセッションで有効にするには、コマンド set -o noclobber
ないし set -C
を使用します。
$ set -o noclobber $ cat /proc/cpu_info 2>/tmp/error.txt -bash: /tmp/error.txt: cannot overwrite existing file
現在のセッションの noclobber
オプションを無効化するには、set +o noclobber
ないし set +C
を実行します。noclobber
オプションを永続化するには、ユーザーのBashプロファイルまたはシステム全体のプロファイルにコマンドを置く必要があります。
リダイレクトされたデータを既存のコンテンツに追記するには、2つの大なり記号 >>
を使います。この機能は、noclobber
オプションを有効にしても禁止できません。
$ cat /proc/cpu_info 2>>/tmp/error.txt $ cat /tmp/error.txt cat: /proc/cpu_info: No such file or directory cat: /proc/cpu_info: No such file or directory
前の例では、ファイル /tmp/error.txt
に、新しいエラーメッセージが追加されました。ファイルが存在していない場合は、新しいファイルが作成されます。
プロセスの標準入力に割り当てられるデータソースを切り替えることもできます。プロセスのstdinに、ファイルのコンテンツをリダイレクトするためには、小なり記号 <
を使います。この場合、データは右から左に流れます。小なり記号の左にリダイレクトするファイル記述子0があるものと見なされ、右側にデータソース(ファイルのパス)を指定します。次に示す uniq
コマンドは、ほとんどのテキスト処理ユーティリティと同様に、デフォルトでstdinに送られるデータを読み込みます。
$ uniq -c </tmp/error.txt 2 cat: /proc/cpu_info: No such file or directory
uniq
に -c
オプションを指定すると、テキストにある同一行の出現回数を表示します。リダイレクトされたファイル記述子が省略されているので、このコマンド例は uniq -c 0</tmp/error.txt
と同等です。入力リダイレクトで 0
以外のファイル記述子を使用することは、原則としてありません(ファイル記述子 3
や 4
を読み取る特別なプログラムでのみ意味があります)。技術的には、データ入出力に3以上のファイル記述子を使用するプログラムを作成することもできます(が、一般的でありません)。たとえば、次のCコードは、ファイル記述子 3
からデータを読み取り、それをファイル記述子 4
にコピーします。
Note
|
プログラムは、そのようなファイル記述子を正しく処理しなくてはなりません。誤った無効な読み書きによって、クラッシュする可能性があります。 |
#include <stdio.h> int main(int argc, char **argv){ FILE *fd_3, *fd_4; // Open file descriptor 3 fd_3 = fdopen(3, "r"); // Open file descriptor 4 fd_4 = fdopen(4, "w"); // Read from file descriptor 3 char buf[32]; while ( fgets(buf, 32, fd_3) != NULL ){ // Write to file descriptor 4 fprintf(fd_4, "%s", buf); } // Close both file descriptors fclose(fd_3); fclose(fd_4); }
このプログラムをテストするには、サンプルコードを fd.c
として保存し、gcc -o fd fd.c
でコンパイルします。このプログラムを実行するには、ファイル記述子3から読み込み、ファイル記述子4に書き出せるようにする必要があります。例として、ファイル記述子 3
に以前に作成しファイル /tmp/error.txt
を割り当てて読み込ませ、ファイル記述子 4
をstdoutにリダイレクトしましょう。
$ ./fd 3</tmp/error.txt 4>&1 cat: /proc/cpu_info: No such file or directory cat: /proc/cpu_info: No such file or directory
プログラマーの観点からは、ファイル記述子を使用することで、オプションの解析やパス名の処理を省くことができます。その際には、たとえば 3<>/tmp/error.txt
のように小なり記号と大なり記号の両方を使用して、読み書き両用のファイル記述子を定義します。(訳注: 2004年以前のバージョンのbashでは利用できません。)
ヒアドキュメントとヒア文字列
入力をリダイレクトする別の方法として、ヒアドキュメント と ヒア文字列 があります。ヒアドキュメントでは、複数行のテキストをリダイレクトされたコンテンツとして入力できます。2つの小なり記号 <<
が、ヒアドキュメントによるリダイレクトを示します。
$ wc -c <<EOF > How many characters > in this Here document? > EOF 43
2つの小なり記号 <<
の後には、終了を示す単語としてここでは EOF
を指定しています。終了を示す単語のみの行を入力すると、入力が終了します。別の単語を終了の印として使用できます。この例では、wc -c
コマンドのstdinに、2行のテキストが送信されて、その文字数が表示されます。入力リダイレクトと同様に、ファイル記述子を省略した場合は、stdin(ファイル記述子 0
)と見なされます。
ヒア文字列は、ヒアドキュメントによく似ていますが、主に1行のみの入力に使用します。
$ wc -c <<<"How many characters in this Here string?" 41
この例では、wc -c
のstdinに3つの小なり記号の右側の文字列が送信され、その文字数がカウントされます。文字列に空白が含まれる場合は、引用符で囲みます。そうしないと、最初の単語のみがヒア文字列として使用されて、残りの単語はコマンドの引数として渡されます。
演習
-
cat
コマンドは、テキストファイルだけではなく、ブロックデバイスの内容などのバイナリデータを別のファイルに送ることもできます。cat
とリダイレクトを使用して、デバイス/dev/sdc
の内容を、現在のディレクトリのsdc.img
ファイルに送るにはどうしますか? -
date 1> now.txt
コマンドでリダイレクトされたチャネルの名前は何ですか? -
リダイレクトを使用してファイルを上書きしようとした時に、
noclobber
オプションが有効になっていることを通知するエラーが表示されました。現在のセッションでnoclobber
オプションを無効にするにはどうしますか? -
cat <<.>/dev/stdout
を実行するとどうなりますか?
発展演習
-
cat /proc/cpu_info
コマンドを実行すると、/proc/cpu_info
が存在しないというエラーメッセージが表示されました。cat /proc/cpu_info 2>1
を実行すると、エラーメッセージはどこにリダイレクトされますか? -
シェルセッションで
noclobber
オプションが有効になっている場合、/dev/null
にコンテンツを送信して捨てることはできますか? -
echo
を使用せずに、変数$USER
の内容を、sha1sum
のstdinにリダイレクトするにはどうしますか? -
Linuxでは、IDが PID であるプロセスによって開かれたすべてのファイルへのシンボリックリンクが、
/proc/PID/fd/
ディレクトリに置かれます。そのディレクトリを使用し、nginx
のログファイルがどこにあるかを調べるにはどうしますか?nginx
のPIDは1234
であると仮定します。 -
単純な算術計算はシェルの組み込みコマンドだけで行えますが、浮動小数点計算には、
bc
(basic calculator)などの特別なプログラムを使います。bc
ではscale
パラメータをを使用して小数点以下の桁数を指定することができますが、通常は対話モードの標準入力からしか、それらのパラメータや数式を受け付けません。ヒア文字列を使って、bc
の標準入力に計算式scale=6; 1/3
を送るにはどうしますか?
まとめ
このレッスンでは、標準的な入出力チャネルをリダイレクトしてプログラムを実行する方法について説明しました。Linuxプロセスは所定の ファイル記述子(0から2)に標準的なチャンネルが接続されているものとしてデータを読み書きし、シェルがそれらのチャンネルを任意のファイルやデバイスに変更します。このレッスンでは、次を説明しました。
-
ファイル記述子とは何か、また、Linuxにおけるその役割。
-
プロセスの標準通信チャネル: stdin、stdout、stderr。
-
入力と出力の両方でデータをリダイレクトしてコマンドを実行する方法。
-
ヒアドキュメント と ヒア文字列 を使用する入力のリダイレクト方法。
以下のコマンドと手順を取り上げました:
-
リダイレクト演算子:
>
、<
、>>
、<<
、<<<
-
コマンド:
cat
、set
、uniq
、wc
演習の解答
-
cat
コマンドは、テキストファイルだけではなく、ブロックデバイスの内容などのバイナリデータを別のファイルに送ることもできます。cat
とリダイレクトを使用して、デバイス/dev/sdc
の内容を、現在のディレクトリのsdc.img
ファイルに送るにはどうしますか?$ cat /dev/sdc > sdc.img
-
date 1> now.txt
コマンドでリダイレクトされたチャネルの名前は何ですか?標準出力またはstdout
-
リダイレクトを使用してファイルを上書きしようとした時に、
noclobber
オプションが有効になっていることを通知するエラーが表示されました。現在のセッションでnoclobber
オプションを無効にするにはどうしますか?set +C
またはset +o noclobber
-
コマンド
cat <<.>/dev/stdout
の結果はどうなりますか?Bashはヒアドキュメントの入力モードに入り、ピリオドのみを入力すると終了します。入力されたテキストはstdoutにリダイレクトされて、画面に表示されます。
発展演習の解答
-
cat /proc/cpu_info
コマンドを実行すると、/proc/cpu_info
が存在しないというエラーメッセージが表示されました。cat /proc/cpu_info 2>1
を実行すると、エラーメッセージはどこにリダイレクトされますか?現在ディレクトリの
1
という名前のファイル。 -
シェルセッションで
noclobber
オプションが有効になっている場合、/dev/null
にコンテンツを送信して捨てることはできますか?できます。
/dev/null
はnoclobber
の影響を受けない特別なファイルです。 -
echo
を使用せずに、変数$USER
の内容を、sha1sum
のstdinにリダイレクトするにはどうしますか?$ sha1sum <<<$USER
-
Linuxでは、IDが PID であるプロセスによって開かれたすべてのファイルへのシンボリックリンクが、
/proc/PID/fd/
ディレクトリに置かれます。そのディレクトリを使用し、nginx
のログファイルがどこにあるかを調べるにはどうしますか?nginx
のPIDは1234
であると仮定します。ls -l /proc/1234/fd
を実行して、ディレクトリ内のシンボリックリンクが指すファイルを調べます。 -
単純な算術計算はシェルの組み込みコマンドだけで行えますが、浮動小数点計算には、
bc
(basic calculator)などの特別なプログラムを使います。bc
ではscale
パラメータを使用して小数点以下の桁数を指定することができますが、通常は対話モードの標準入力からしか、それらのパラメータや数式を受け付けません。ヒア文字列を使って、bc
の標準入力に計算式scale=6; 1/3
を送るにはどうしますか?$ bc <<<"scale=6; 1/3"