My Day One

毎日東京の隅っこでコードを書いているエンジニアのブログです。

標準入出力・ファイル・リダイレクト・パイプについて

はじめに

この記事は私の知識の整理のために、標準入出力・ファイル・リダイレクション・パイプについてまとめたものです。 webアプリの開発をするときにはそこまで意識しないことではありますが、開発者としては抑えておくべき基礎なので、それを自分なりに、他人に説明するくらいのスタンスでまとめてみました。 間違いなどあればご指摘ください!それでは書いていきます。

標準入力

これは、コンピューター上で実行されているプログラムが、何も特別な指定がされていない場合に利用するデータの入力元のことを言います。 少し分かりにくいと思うので、コマンドラインを例にとると

$ ls

とすると、結果が返ってきますよね。この ls が標準入力です。

標準出力

先ほどの標準入力(ls)を入力してEnterキーを押すと

Applications
Creative Cloud Files
Desktop
Documents
Downloads

みたいな感じでその結果が返ってきます。これが、標準出力です。 標準出力は、標準入力がプログラムに渡り、そこから出力される結果ということですね。

10 + 1 = 11 みたいな式があるとしたら、 10と1が標準入力、 10 + 1という演算がプログラム、 11が標準出力 といった具合です。

標準エラー出力

主にプログラムのエラーを表示するために使用されます。 ls コマンドで存在しないファイルを指定した場合、下記のようなエラーメッセージが表示されます。このエラーメッセージは標準エラー出力に表示されています。

$ ls /aaaaaa
ls: /aaaaaa: No such file or directory

ファイル

コンピューター上で管理される情報の単位のこと。ファイルの名前は人間が識別しやすいように名前がつけられています。sample.txthoge.html のようなものがそれに当たります。 ファイルはそれ単体でもコンピューター上に存在するが、XX関連のファイルをまとめておきたい、という場合にフォルダという単位でファイルを格納することもできます。

また、ファイルは主に、プログラムファイル、データファイルに分かれます。 プログラムファイルはJavascriptRubyなどで書かれたファイル(例えば sample.js sample2.rb など)を指し、データファイルはpngやjpgの画像ファイルなど、それ自体はコンピューター上で実行されず、プログラムによって読み込みまれるようなものを指します。

フォルダも管理する上での単位という意味ではファイルと同じですが、フォルダは「フォルダの中にファイルを入れることができ、かつ、フォルダも入れることができる」というのが1つの特徴です。

テキストファイル・バイナリファイルという分類もあり、テキストファイルは人間が識別できる文字や数字で書かれたファイルのことで、バイナリファイルは人間では読めない形式(コンピューターが読む前提の形式)でのファイルのことになります。

リダイレクト

標準入力、標準出力先を変更する機能のこと。 例えば cat コマンドは

$ cat file_name

のようにしてfileの中身を表示しますが、その出力先を変えたり、標準入力の元ファイルを変更したりするのに使うことができます。

リダイレクトは主に >< を使用して入出力をコントロールします。

例えば、ls -l コマンドで表示できる内容を、コマンドライン上に出力するのではなく、result.txt に送りたい、という場合は

ls -l list.txt

とします。これで、ls -l の結果が result.txt に全て入るようになります。書き込まれたかどうか確認するには

cat result.txt

で確認できます。

ただし、標準エラー出力

$ ls /aaaaaa > list.txt
ls: /aaaaaa: No such file or directory

のように単純にリダイレクトしても、コマンド上に表示されてしまうので

$ ls /aaaaaa 2> list.txt

のように 2> をリダイレクトに使うことでファイル内に格納します。

また、「コマンドの結果とエラーメッセージを別々のファイルに出力したい」というケースもあるかと思いますが、その場合は

$ ls /aaaaaa > list.txt 2> error.txt

のようにします。これは、実行結果は list.txt へ、エラーは error.txt へと送られるコマンドです。1行で書けて便利ですね!

コマンドの結果とエラーメッセージを合わせて1つのログファイルにしたい!というときは

$ ls / aaaaaa > result.txt 2>&1

のようにすることで、1つのファイルにまとめて出力できるようになります。

コマンドの意味合いとしては、最初の > が標準出力先を result.txt に設定するためのコマンド、次の 2>標準エラー出力の設定先を設定するためのコマンド、最後の &1 が標準出力と同じファイルへ出力先を指定するためのコマンド、といった具合です。2>&1 で1つのセットとしても良いかもしれません。これも便利です。

リダイレクトをすると、指定したファイル名と同じファイル名がある場合には、特別な設定を何もしなければ、その内容が上書きされてしまいます。

$ echo 10000 > number.txt
$ echo 99999 > number.txt

として、catで内容を表示すると

$ cat number.txt
99999

となり、元の10000は上書きされてしまいます。

上書きを防ぐには >> を使って、ファイルの末尾に追加する、というような処理を使います。

また、シェルのオプションで noclobber というオプションを使うとファイルの上書きの禁止ができるので、それを設定して上書きをさせない設定にするというのも1つの手段です。

パイプ

コマンドは1つ入力して出力させておしまいではありません。複数コマンドをつなげて、より複雑なこともできます。例えば、/binフォルダの中にあるファイルの、ファイルサイズが大きい順の3つを取得する、というようなこともできたりします。

それを実現するために必要なのが、パイプという機能です。

まずは例を見てみましょう。

$ ls -l / > sample_list.txt
$ less sample_list.txt

これでも結果を表示することはできますが、1行で書く方法でパイプを使用します。

$ ls -l | less

この例を見れば想像がつくかもしれませんが、パイプ(記号で表すと | )によって、前のコマンドの結果を次のコマンドに渡すことができるということです。実行結果を渡せるのは便利ですね。

このパイプを、フィルタと呼ばれる標準出力を入力として受け取り、標準出力に出力するコマンドと一緒に使うことで、/binフォルダの中にあるファイルの、ファイルサイズが大きい順の3つを取得する、ということを実際に書いてみます。

使うコマンドは3つで、 du、sort、head のみです。

  • duコマンド→-kオプションを渡すことで、ファイルのサイズをキロバイト数で表示する
  • sortコマンド→昇順(今回は小さい順)に並べる。デフォルトはアルファベット順だが、-nオプションをつけることでサイズ順に並べることができる。-rオプションをつけると逆順になる
  • headコマンド→上から順に取得した結果を表示する -n 3のようにオプションを渡すと上から3つを取得できる

という特徴を使ってコマンドを書くと

$ du -k /bin/* | sort -n -r | head -n 5

となり、出力結果は

720 /bin/ksh
368 /bin/zsh
340 /bin/bash
260 /bin/tcsh
260 /bin/csh

となります。キロバイト数の大きい順に表示され、やりたいことも達成できました。

コマンドラインはあまりちゃんと勉強してこなかったので、勉強すると基礎でも奥が深いなと感じました。