Linuxターミナル、コマンドtips その3: stdin, stderr, stdout, リダイレクト, パイプ
(2019/06/29追記) 実践的、網羅的かつ簡潔にまとまったドキュメントを見つけたのでメモ(日本語訳もある)*1。
このtipsはこれからLinuxを使っていく必要がある人、特に端末操作に苦戦している人、もしくは端末操作に対して嫌悪感すら抱いている人に向けて書いたものです.作成の経緯はその1の冒頭および注釈に書きました.
前回の話題
- ファイルを見つける(locate, find)
- ファイルの中身を調べる(grep)
Linuxターミナル、コマンドtips その2: ファイルを見つける(locate, find)、ファイルの中身を調べる(grep)
今回の話題
- コマンドや自作プログラムの入出力をコントロールする
- stdin, stdout, stderrの理解とターミナル、キーボードとの関係
- リダイレクト: ファイルから読み出す、ファイルに書き出す
- パイプ: コマンドの入出力をを組み合わせる
標準入出力(stdin, stdout, stderr)の理解とターミナルとの関係
まずは標準入出力とプログラムとの関係を再確認
linuxにおいて、あらゆるプログラム(コマンド、Cプログラム、shellscript、C/C++/Java/Ruby/Perl/Pythonプログラム、etc...)の入出力は以下のように説明できる。
標準入力(stdin:0) ---> プログラム ---+---> 標準出力(stdout:1) | +--> 標準エラー出力(stderr:2)
- これまで扱ってきたcat, ls, pwd, locate, find, grep等々、あらゆるコマンドは本質的にCプログラムをコンパイルした実行ファイルと変わらない
- hello world!をprintfで出力するプログラムも、上記のコマンドと同じディレクトリに保存すればコマンドと同じように呼び出せる
- 出力が2つに分かれている点に注意
参考: ふつうのLinuxプログラミング
上記のリンク先の連載(全4回)もしくは書籍を是非読んで欲しい.個人的に学習の初期で最も役に立った本の一つ.

ふつうのLinuxプログラミング Linuxの仕組みから学べるgccプログラミングの王道
- 作者: 青木峰郎
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2005/07/27
- メディア: 単行本
- 購入: 35人 クリック: 450回
- この商品を含むブログ (150件) を見る
標準入出力とターミナル、キーボードとの関係
あるコマンドを実行すると、ターミナルにその出力が表示されるが、それは標準入出力とターミナルの関係が以下のようになっているから。
キーボード ---> 標準入力(stdin:0) ---> プログラム ---+--> 標準出力(stdout:1) --------+---> ターミナル(shell) | | +--> 標準エラー出力(stderr:2) ---+
- いまいち実感しにくいけど、標準入力はキーボードに紐付けられている
- stdoutと、stderrが両方ともターミナル上に出力される点に注意
リダイレクト
プログラムを実行するとターミナル上に表示されるのは確認したが、それは単にデフォルトの出力先がターミナルというだけであって、linuxにおいては、その出力先を簡単にファイルに切り替えることができる.
stdout: 出力をリダイレクトする
すでに述べたように、プログラム自体はコマンドだろうが、自分で組んだプログラムの実行ファイルだろうが何でも良いが、ココでは例としてlsの出力をファイルに保存してみる。
$ ls > output-ls #lsコマンドの標準出力(stdout)をoutput-lsというファイルにリダイレクト $ ls 1> output-ls #上記と全く同じ意味、stdoutの番号(ファイルディスクリプタ)は1番 $ cat output-ls #ファイルの中身を確認
ターミナル上では、横並びに表示されるのに、ファイル内部では行区切りで一つ一つ保存されていることが確認できる(これは後で示すパイプによって、他のコマンドと組み合わせ易くするため)
stdout: 既存のファイルに出力を追記する
上記で生成したoutput-lsという既存ファイルに、更に出力を追記するには>>
を使う。
$ ls > output-ls #lsコマンドの標準出力(stdout)をoutput-lsというファイルにリダイレクト $ ls >> output-ls #lsコマンドの標準出力(stdout)をoutput-lsというファイルに追記 $ cat output-ls #ファイルの中身を確認、追記されている $ ls > output-ls #すでに存在するファイルに対しては上書きとなるため、元のデータが失われる
よくある致命的な失敗
>>
で追記するべき所で、>
で上書きしてしまい、元のデータを全てふっ飛ばす
stdin: 標準入力からデータを入力する
他の入力を必要とするコマンドをも同様だが、例えばcatやgrepコマンドは、引数に入力データ(=パターンマッチの対象)となるファイルを指定することもできるし、標準入力からデータを渡すこともできる
$ cat $ grep 'hoge'
上記を実行すると、ctrl+dを押すまで、catやgrepにキーボード(=stdin)からデータを一行ずつ渡すことができる
catは単に入力文字をそのまま出力するし、grepはパターンがマッチした場合にだけ入力行を出力する
stdin: 標準入力データをリダイレクトで指定する
当然、catやgrepで上記のように一行一行入力するなどと言ったことは通常行わないため、通常ファイル単位でまとまったデータをコマンドに渡す
<
で入力データファイルを指定することができる
$ grep 'hoge' < target_file.txt $ grep 'hoge' 0< target_file.txt #上記と全く同じ意味
なんとなく、target_file.txt > grep 'hoge'
などとしてしまいそうだけど、左向きの山カッコじゃないとダメなので注意(シェルは入力されたコマンドを前から順に解釈する)
stderr: 標準エラー出力を確認する
例えば、grepコマンドを単体で実行するとエラーメッセージが表示される
$ grep 使用法: grep [OPTION]... PATTERN [FILE]... Try 'grep --help' for more information.
これを>
でリダイレクトしてもエラーメッセージがファイルに書き込まれない
$ grep > output-grep 使用法: grep [OPTION]... PATTERN [FILE]... Try 'grep --help' for more information. $ cat output-grep #ファイルは空
このエラーメッセージは、標準エラー出力(stderr)である
標準エラー出力の番号(ファイルディスクリプタ)は2なので2>
としてやれば良い
$ grep 2> output-grep $ cat output-grep #ファイルに書き込まれていることを確認
stdoutとstderrを操る
stdoutとstderrを別々のファイルに書き出す
commandは任意のプログラム
command > output-stdout 2> output-stderr
stdoutとstderrを1つのファイルにマージする
command > output-stdout-stderr 2>&1
必ず出力先のファイルを指定した後に2>&1
とする
stdoutとstderrを両方とも捨てる
command > /dev/null 2>&1
/dev/nullはあらゆる出力を無に帰すブラックホール
不要な出力を捨てる宛先としてよく用いられる
stdoutは捨てて、stderrをstdoutとして出力する
command 2>&1 > /dev/null
catコマンドやgrepコマンドのソースコードを読む
下記のgit cloneを叩いてソースコードを入手
$ git clone git://git.sv.gnu.org/coreutils
普段使っているコマンドは特別なものでもなんでもなく、単なるCのプログラムであることがわかる
小休止: 2014/08/04のrmコマンドのバグフィックス
2014/08/04、rm --help
の内容が修正されていたのだが、すぐに確認可能な小さなヘルプメッセージのバグ?だったので紹介
Date: Mon Aug 4 11:42:45 2014 +0100 doc: indicate that FILE arguments are optional with rm -f * src/rm.c (usage): s/FILE/[FILE]/. * Fixes http://bugs.gnu.org/18187 *
手元のrmコマンドのバージョンが古いので、上記の修正前のヘルプが表示された
$ rm --help Usage: rm [OPTION]... FILE... Remove (unlink) the FILE(s).
git log -p
で変更履歴を溯ると面白いかもしれない
パイプ
要望: あるコマンドの結果を他のコマンドの入力としたい
よくある話
リダイレクトの知識があれば、この要望はファイルを介して簡単に解決できる
$ ls > output-ls #stdoutをoutput-lsというファイルに書き出す $ grep 'mycat' < output-ls #output-lsをgrepのstdinとする $ grep 'mycat' output-ls #もしくは、ファイルを引数で指定してやる
でもメンドクサイ
しかも、いちいちgrepの入力となる一時ファイル(output-ls)が生成されてウザイ
解法: コマンドの出力(stdout)を直接他のコマンドの入力(stdin)とする
|
でできる
command1 | command2
データの流れをイメージしよう
以下のようになっている
stdin ---> command1 ---> stdout | stdin ---> command2 ---> stdout | | +---> stderr +---> stderr
stdoutとstderrの両方をパイプで渡す
command1 2>&1 | command2
stderrだけをパイプで渡す
command1 2>&1 > /dev/null | command2
パイプの威力
パイプはいくらでも繋げられる(重要)
工夫次第で壮絶に煩わしい作業がたったの一行で済んでしまうことも多々ある
集計、ソート、出力の制御を同時にやる
hoge, fuga, foo, barがランダムに並んでいるファイル(random-data.txt)を集計して結果をターミナル上に表示しながらファイルに記録したい場合、
$ cat random-data.txt | sort | uniq -c | tee sorted-data.txt 6000 bar 5000 foo 4000 fuga 3000 hoge
tee
コマンドは受け取ったデータを指定したファイルに書き込みつつ、ターミナル上に表示する便利なコマンド
既存のファイルに追記したい場合は、tee -a sorted-data.txt
という感じで、-a
オプションを用いると良い
シェル芸
上記のような、一行で処理が簡潔するようなプログラムをワンライナーとか一行野郎などと言ったりする
このワンライナーのドリル的なものがあるので紹介
まとめと補足
- 普段使っているコマンドはシンプル
- stdin, stdout, stderrとコマンドとの関係が頭の中で整理されると、リダイレクトやパイプを気軽に使えるようになるはず
- linux(Unix系OS)におけるコマンドは行志向
- 自作プログラムの出力もプレーンテキストかつ行志向なものにしておくと、既存のコマンドとの組み合わせが簡単にできるようになる
- パイプの可能性は無限大
- 気軽に試せて結果をすぐに確認できる
- 複数繋げてみて結果がおかしかったら、途中までで一旦ターミナル上に出力させてみて、思った通りになっているかチェックしてみる
- シェルという大きな存在を忘れない
- 今回、シェルについてはほとんど触れてないが、実際にはターミナル上で入力したコマンドは全てシェルというプログラムによって解釈、実行されている
- 例えば、cdやhelpなどは、catやgrepなどの独立したプログラムではなく、シェル(bash,zsh,...)というプログラムに組み込まれた所謂組み込みコマンドである
- 組み込みコマンドの一覧は、ターミナル上で
help
と打てば表示される
- 組み込みコマンドの一覧は、ターミナル上で
- 参考: カーネルとシェルとターミナルの関係
- 参考: Linux上でシェルが実行される仕組みを,体系的に理解しよう (bash 中級者への道)
- 全てはファイルとして抽象化されている
- キーワードは、バイトストリーム
- stdin, stdout, stderrはファイルディスクリプタ
- それぞれ、0, 1, 2が割り当てられており、リダイレクトで指定している番号はファイルディスクリプタの番号
- 標準入出力は、単にデフォルトがキーボードやディスプレイに割り当てられている特別なファイルと見ることができる
- ファイルとして抽象化されているので、キーボード(stdin)に対してfgetsなどとしても動作する
- コマンドライン引数で渡しているファイルは、3番以降のファイルディスクリプタを割り当てて開いている
- ネットワークプログラミングにおけるソケットもファイルとして抽象化されており、ファイルディスクリプタを割り当てて扱う
- だからコネクションを張ったソケットに対してfgetsなどとしても問題なく動作する
- 参考: Linuxカーネルの基本機能 第12回 ソケット・インタフェース
- 参考: まつもと直伝 プログラミングのオキテ 第16回 ネットワーク・プログラミング(ソケット編)
UNIXという考え方
以下を読むとなお良い

- 作者: Mike Gancarz,芳尾桂
- 出版社/メーカー: オーム社
- 発売日: 2001/02/01
- メディア: 単行本
- 購入: 40人 クリック: 498回
- この商品を含むブログ (145件) を見る