7.2 標準入出力
Linux Essentials Online Text | 2021/07/26

標準出力と標準エラー出力

これまでの様々なコマンド実行結果を思い出してみて下さい。コマンドは実行すると裏ではプログラムが起動し、結果が画面に表示されます。また、タイプミスや構文ミスがあるとエラーメッセージが画面に表示されます。どちらも同じ画面表示に見えますが、システム上は違う仕組みで表示されているのです。まずは下の図を見て下さい。

この図の通り、コマンドの実行には「1つの入口と2つの出口」があります。入口は「標準入力」と言い、出口はそれぞれ「標準出力」「標準エラー出力」の2つがあります。
例えば、lsを打つとカレントディレクトリのファイルとディクトリの一覧が表示されます。今までは「画面に表示された」と解説していましたが、これは「“標準出力”としてファイルとディレクトリの一覧が画面に出力された」ということなのです。

標準出力のリダイレクト

先程説明した「“標準出力”としてファイルとディレクトリの一覧が画面に出力された」の「画面」とはシェルのことです。つまり、通常コマンドを実行するとシェルが出力先になっているということです。Linuxのシェル上では、出力先をシェル(つまり画面)だけでなくファイルなどに変更する事が可能で、別の何かに出力することをリダイレクトと言います。

では、dateコマンドを使ってリダイレクトしてみます。
dateは、システムの日時を表示するコマンドです。実行すると日時が表示されます。

$ date
2021年  3月 22日 月曜日 17:55:13 JST

次のように入力してみてください。

$ date > date.txt
$

先程、dateコマンドを実行した時に表示されていた日時が、リダイレクトしたことで「date.txt」に格納されたため、表示されなくなったことに注目してください。
そして、リダイレクト先として指定した「date.txt」が生成されていることがわかります。

生成された「date.txt」の中身を確認してみます。dateコマンドを実行した時に出力されるはずの内容が、「date.txt」の中に格納されたことがわかります。

$ ls d*
date.txt
$ cat date.txt
2021年  3月 23日 火曜日 07:32:14 JST

では、もう一度同じコマンドを使ってリダイレクトしてみてください。

$ date > date.txt
$ cat date.txt
2021年  3月 23日 火曜日 07:41:02 JST

結果を見ると、時刻が更新されていることがわかります。
リダイレクト先ファイルを指定する際、同名ファイルが存在しなければ新規作成、存在する場合は上書きとなります。

標準エラー出力のリダイレクト

Linuxシステムを操作していると様々なエラーがおきます。システムはエラーを伝えるために、画面に表示します。
試しに/var/log/messagesファイルを閲覧してみます。

$ cat /var/log/messages
cat: /var/log/messages: 許可がありません

/var/log/messagesは、管理者しか閲覧できないファイルですので、一般ユーザ権限では閲覧ができずエラーが表示されます。

では、このエラーメッセージを、error.logにリダイレクトしてみます。

$ cat /var/log/messages > error.log
cat: /var/log/messages: 許可がありません

リダイレクトすると出力結果はファイルに格納されるため、画面上には何も表示されないはずですが、エラーメッセージは変わらず表示されています。

では、何がリダイレクトされたのでしょうか。実際に、error.logファイルの中身を確認してみます。

$ cat error.log
$

何も表示されませんでした。
これはリダイレクト対象となるデータが無かった、ということを意味します。

今回実行している「cat /var/log/messages > error.log」は、「cat /var/log/messages」の「標準出力」を「error.log」に書き込む、という指示をしているコマンドです。

しかし、「cat /var/log/messages」の実行結果は「権限が無いため表示できませんでした。」です。つまり、このコマンドの標準出力は「表示できる情報がなかった」ということです。言い換えると、空のデータが書き込まれた、ということです。
一方で、コマンド実行が失敗した時などに表示されるエラーメッセージは「標準エラー出力」という種類のもので、「標準出力」ではありません。つまり、リダイレクト対象ではない(=画面に表示する)、ということです。

ちなみに、リダイレクト先として指定したerror.logファイルは、エラーかどうかに関係なくファイルとして生成されます。

以上が、エラーメッセージが画面に表示されて、生成されたerror.logファイルの中身が空であることの正体です。

実際に、標準エラー出力をファイルへリダイレクトするには、以下のように入力します。

$ cat /var/log/messages 2> error.log
$
$ cat error.log
cat: /var/log/messages: 許可がありません

通常はどちらも画面に表示されるため、同じコマンド結果に見えてしまいますが、システム的には分けて考えられており、リダイレクトでは「標準出力」は「1」、「標準エラー出力」は「2」と出力先が決まっています。

最初にやった「標準出力」のリダイレクトは本来「1>」と入力すべきなのですが、「1」が省略されているのです。

エラーメッセージは何が起こったかをユーザに通知するためのものですから、リダイレクトなどでメッセージを確認ができなくなってしまう、というのは困りますね。ですので、一般的にエラーメッセージは「標準出力」とは別の「標準エラー出力」に出力で処理するようになっています。これにより、標準出力がファイルへリダイレクトされていても、エラーメッセージを確認することができるように、というわけです。

エラーメッセージのリダイレクトは、夜間のスケジュール処理などで、その場で画面を確認できないケースなどで有効な手段です。

「標準出力」と「標準エラー出力」を同時にリダイレクト

「標準出力」と「標準エラー出力」を混ぜて出力することもできます。

例えば、以下のコマンドを入力し、結果を確認してみましょう。

$ ls /etc/*

/etcディレクトリには、システムに関わるディレクトリですので、ユーザ権限ではアクセスできない場所も含まれており、「標準出力」と「エラー標準出力」が混在した状態となります。

以下のコマンドを入力してみてください。

$ ls /etc/* > redirect.txt

「標準出力」はredirect.txtに格納され、「標準エラー出力」が画面に表示されます。

では、今度は以下のコマンドを入力してみましょう。
コマンドの書式、つまりファイルの指定と1、2の指定の位置を間違えないようにしてください。

$ ls /etc/* > redirect.txt 2>&1
$
$ cat redirect.txt

リダイレクトコマンドを入力した際、「標準エラー出力」が画面に出力されなくなったことに注目してください。
では、redirect.txtの中身を確認してみましょう。「標準出力」と「エラー標準出力」のいずれもが含まれていることがわかります。

なぜ、「2>&1」で「標準出力」と「エラー標準出力」の両方がリダイレクトされるのか。

リダイレクトの1や2の指定方法は少しややこしいので、少し整理しましょう。
まずは、

標準出力    =1
標準エラー出力 =2

である事をしっかり覚えて下さい。 (ちなみに標準入力は 0 番)。

覚えましたか?

それでは始めましょう。
「標準出力」と「エラー標準出力」を1つのファイルにリダイレクトさせるには「2>&1」を、最後に入力します。
以下のようになります。先ほどやったので、わかっているかと思います。

$ ls /etc/* > redirect.txt 2>&1

それでは、分解して見ていきましょう。
まずは、リダイレクトする前の部分についてです。

$ ls /etc/*
⇒ 1の出力先 … 画面
⇒ 2の出力先 … 画面

これは問題ないですね。リダイレクトする前ですから、いずれも画面に表示されます。
では、続いて「標準出力」をリダイレクトさせる所までについてです。

$ ls /etc/* > redirect.txt
⇒ 1の出力先 … file
⇒ 2の出力先 … 画面

「1」が省略されているだけで「標準出力」のリダイレクトです。
ですから、「標準出力」はファイルに、「標準エラー出力」は画面に出力されます。

では最後。全文にした場合を考えてみますと、以下の通りになります。

$ ls /etc/* > redirect.txt 2>&1
⇒ 1の出力先 … file
⇒ 2の出力先 … 1の出力先と同じ

「2>&1」とは「2の出力先を1の出力先と同じものにする」という意味です。
「2>&1」が付与される前の段階で「標準出力」の出力先が「redirect.txt」と決められています。その上で「2>&1」と言う指示ですので、「標準出力」と同じ「redirect.txt」に出力されることになります。

もし、これが誤って「1>&2」と指定してしまった場合も考えてみましょう。

意味としては「1の出力先を2の出力と同じものにする」という意味になります。
「1>&2」を指定する直前の処理は、以下でしたね。

$ ls /etc/* > redirect.txt
⇒ 1の出力先 … file
⇒ 2の出力先 … 画面

「1>&2」は、1の処理は2と同じ、ですから、どちらも画面に表示されるようになってしまいます。
1と2がどっちを指定すればよいかわからなくなったら、順序だてて考えてみましょう。

また、このようにコマンドの間に入力するような記載もNGです。

$ ls /etc/* 2>&1 > redirect.txt

「ls /etc/* 2>&1」の段階で、1の出力先は画面です。

つまり、「標準エラー出力」は画面に表示されます。その後「標準出力」は「redirect.txt」にリダイレクトされます。この場合は、「2>&1」を入れている意味がありません。

様々なリダイレクト

追記のリダイレクト

リダイレクトを行う場合にファイルを指定しますが、この時指定するファイルが存在するかどうかで動作が違います。
例えば、以下のように実行します。

$ date > date.txt
$ cat date.txt

この時、カレントディレクトリに「date.txt」が存在しなければ、「date.txt」が新規作成されます。

もし、カレントディレクトリに「date.txt」が存在する場合は「date.txt」が上書きされます。「date.txt」はdateコマンドの実行結果ですから、実行するたびに中身の時刻が更新されていくのがわかると思います。

上書きではなく、追記したい場合は「>」を2つ重ねて実行します。

$ date >> date.txt
$ cat date.txt

上記のように実行すると、dateの実行結果が「date.txt」のファイルの末尾に追記されます。

入力のリダイレクト

リダイレクトには出力だけではなく入力も存在します。
例えば、

$ cat < date.txt

とコマンドを実行すると、catの実行に対して「date.txt」の内容を入力します。
つまり、「date.txt」の内容をcatコマンドで実行する、という意味になり、

$ cat date.txt

と同じ意味になります。

この例では、あまり意味がないように見えるかもしれませんが、cpioのようにリダイレクトを利用しなければならないコマンドもあります。入力方向でも使用できることを覚えておきましょう。

リダイレクトを使ったファイル作成

今度はcatコマンドとリダイレクトを組み合わせて、自由な内容でファイルを作成します。

下記のように、catコマンドでls-redirectへリダイレクトすると、入力が可能になります。ここにHello~を入力して[Ctrl]+[d]を押します。

$ cat > ls-redirect
Hello
This is cat redirect		← redirectまで入力したら、[Ctrl]+[d]を押す
$
[Ctrl]+[d]はEOF(End Of File)を示すキーで「データ入力の終わり」を示します。Linuxではデータの読み込みが最後になると、このEOFが入力して終える決まりがあります。今回はEOF([Ctrl]+[d])を手動で入力する事で、システムは入力が終わったと判断しリダイレクトを終了します。

※ [Ctrl]+[c]というキーバインドもあります。このキーバインドに処理を中断させる機能があります。例えば、処理が思ったような動きをせず中止したいときは、シェル上で[Ctrl]+[c]を押すことでプログラムが停止して処理が中断されます。

なお、上記のように実施することも可能です。

$ cat << EoF > redirect_eof
> Hello
> This is cat redirect
> EoF
$ cat redirect_eof
Hello
This is cat redirect

「<<」は終了文字を指定します。上記では「EoF」を終了文字としています。つまり、「EoF」を入力するまでの間に入力した内容を「redirect_test」にリダイレクトする、という意味です。
上記は例として「EoF」を終了文字としましたが、実際には何を指定しても構いません。

以下は、終了文字を「end」として実行しています。

cat << end > redirect2
> Hello
> This is cat redirect2
> end
$ cat redirect2
Hello
This is cat redirect2

パイプ

パイプはコマンド同士を組み合わせて、より効率的に作業するために用いる手法の一つです。
本来、標準されるはずの結果を画面に出力せずに、別のコマンドに渡すことができます。


例えば、以下のコマンドを入力してみましょう。

$ ls -l /usr/bin

画面にファイル一覧が表示されます。しかし、/usr/binにはたくさんのコマンドが存在し画面に表示しきれないため、最初の方に表示されたコマンドが流れてしまい確認することができません。

では、以下のようなコマンドを入力してみましょう。

$ ls -l /usr/bin | less

※「|」は[Shift]+[\]で表示させる事ができます。

lessは前章で出てきたページャのlessコマンドで、[q]を入力すれば終了できます。

「|(パイプ)」は標準出力と標準入力を「つなげる」役割をします。上記のコマンドでは、lsコマンドにより標準出力されるべき結果(ファイルの一覧)を、lessコマンドの標準入力に「つなげ」ています。
less コマンドには、標準入力からのデータをページングして表示する、という機能がありますので、lsコマンドで出力された結果(画面に表示し切れなかった内容を含む)をlessコマンドでページングする事が出来るようになります。

前項で説明した、標準エラー出力や標準出力と混ぜて出力する事もできます。
以下のように入力して下さい。

$ ls -l xxx /usr/bin 2>&1 | less

lsコマンドは最初に「xxx」、次に「/usr/bin」と順番に処理します。「xxx」は存在しないため、エラーメッセージが標準エラー出力で出力され、その後に「/usr/bin」が標準出力で出力されることになります。
今回は「2>&1」が指定されているため、どちらも同じ場所に出力されることになり、その結果をlessで確認することができます。

なお、パイプはいくつでも連結させることができます。

2つ以上のパイプが使用される場合も考え方は同じです。左から順に処理が行われ、パイプが入力されるたびに、左側で処理された標準出力が次のパイプの後に書かれたコマンドに渡されていきます。

Linuxではコマンドとパイプをうまく組み合わせることで、テキストの結果を加工していくことが可能です。

$ dmesg | grep Network
[    2.147276] e1000: Intel(R) PRO/1000 Network Driver - version 7.3.21-k8-NAPI
[    2.981592] e1000 0000:00:03.0 eth0: Intel(R) PRO/1000 Network Connection
$ dmesg | grep Network | grep Driver
[    2.147276] e1000: Intel(R) PRO/1000 Network Driver - version 7.3.21-k8-NAPI

grepは、指定した文字列を含む行を抜き出すコマンドです。
dmesg(起動ログ)の実行結果から、「Network」を含む行を抜き出し、更にそこから「Driver」を含む行を抜き出しています。

PAGE TOP