標準入出力とリダイレクションを使いこなす

Linuxのコマンドは標準入力標準出力を使用して動作しています。一般的に、標準入力にはキーボードが使われ、標準出力はターミナルの画面が使われます。

キーボードからコマンドなどの入力を受け取り、コマンドの実行結果を画面に表示していることになります。

また、標準入力と標準出力に加え、エラーメッセージを表示するための標準エラー出力が用意されています。

この3つをあわせて標準入出力といいます。

通常は入力装置がキーボードで出力装置が画面ですが、入力先や出力先をファイルに変更することもできます。これをリダイレクションまたはリダイレクトといいます。

入出力チャネル数値通常リダイレクト
標準入力(stdin)0キーボードからの入力ファイルからの入力
標準出力(stdout)1ディスプレイへの出力ファイルへの出力
標準エラー出力(stderr)2ディスプレイへの出力ファイルへの出力

出力リダイレクション

標準出力をディスプレイからファイルに切り換えることを出力リダイレクションといい、大なりの不等号記号を使用して次のように使用します。

コマンド > ファイル名

lsコマンドで動作を確認してみましょう。

まずは普通にlsコマンドを実行します。

$ ls -l
total 1856
-rw-rw-r-- 1 ubuntu ubuntu 0 Dec 16 12:58 2
-rw-rw-r-- 1 ubuntu ubuntu 72 Mar 5 08:05 hello.go
-rw-rw-r-- 1 ubuntu ubuntu 59 Mar 5 07:15 index.html
-rwxr-xr-x 1 root root 1880089 Mar 5 08:06 myapp
drwxrwxr-x 2 ubuntu ubuntu 4096 Feb 14 04:59 share
drwxr-xr-x 3 ubuntu ubuntu 4096 Dec 14 08:39 snap

今度はlsコマンドの結果をリダイレクトしてls.txtに出力します。

$ ls -l > ls.txt
$ (何も表示されません)

この場合、出力が完了してもターミナル画面には何も表示されません。
catコマンドでls.txtの内容を確認します。

$ cat ls.txt
total 1856
-rw-rw-r-- 1 ubuntu ubuntu 0 Dec 16 12:58 2
-rw-rw-r-- 1 ubuntu ubuntu 72 Mar 5 08:05 hello.go
-rw-rw-r-- 1 ubuntu ubuntu 59 Mar 5 07:15 index.html
-rw-rw-r-- 1 ubuntu ubuntu 0 Mar 12 08:30 ls.txt
-rwxr-xr-x 1 root root 1880089 Mar 5 08:06 myapp
drwxrwxr-x 2 ubuntu ubuntu 4096 Feb 14 04:59 share
drwxr-xr-x 3 ubuntu ubuntu 4096 Dec 14 08:39 snap

echoコマンドでも試してみましょう。

$ echo "Hello World"
Hello World
$ echo "Hello World" > hw.txt
$ (何も表示されません)
$ cat hw.txt
Hello World

このように出力リダイレクションを使うと、コマンドの実行結果をファイルに保存することができます。

ちなみに、catコマンドを引数無しでファイルにリダイレクトすると、キーボードで入力した内容をファイルに保存できます。

catコマンド入力後、エンターキーでキーボードからの入力を待つ状態になります。その状態で何らかの入力を行うと、入力した物と同じ内容が次の行に出力されます。

このモードから抜けるには「ctrl+d」を入力します。

$ cat > cat.txt
catでテキスト入力
してみます。
(ctrl+d)
$ cat cat.txt
catでテキスト入力
してみます。

またヒアドキュメント機能を使うことで、入力の終了を任意の文字列に指定することできます。

コマンド << 終了文字列
> データ
> データ
> データ
> 終了文字列

以下の例では、「<<」の直後に指定する文字列「EOF(end of fileを意味)」を入力した時点で終了し、その内容を標準出力しています。なお、文字列は何でも大丈夫です。

$ cat -n << EOF
> `date`
> $LANG
> my name is $USER
> EOF 1	Sat Mar 13 01:47:49 UTC 2021 2	C.UTF-8 3	my name is ubuntu

ヒアドキュメントの内容をファイルにリダイレクトすることもできます。

$ cat -n << EOF > heredoc.txt
> `date`
> $LANG
> my name is $USER
> EOF
$ cat heredoc.txt 1	Sat Mar 13 01:50:03 UTC 2021 2	C.UTF-8 3	my name is ubuntu

ヒアドキュメントを使うことで、viのようなエディタを立ち上げるまでもないような短い文章やプログラムをファイルに保存することができます。

$ cat << EOF > Dockerfile
> FROM httpd
> COPY index.html /usr/local/apache2/htdocs/
> EOF
$ cat Dockerfile
FROM httpd
COPY index.html /usr/local/apache2/htdocs/

catコマンドは他にも便利な機能があります。catコマンドの使いこなしについてまとめてあるので興味のある方はご覧ください。

追加の出力リダイレクション

大なり記号を2つ連続させるとコマンドの結果を指定のファイルに追記します。

コマンド >> ファイル名

echoコマンドで試してみましょう。

$ echo "Hello Japan" >> hw.txt
$ (何も表示されません)
$ cat hw.txt
Hello World
Hello Japan

すでにファイルが存在する状態で大なり記号を一つにした場合は、既存のファイルが上書きされます。

$ echo "Hello Asia" > hw.txt
$ (何も表示されません)
$ cat hw.txt
Hello Asia

特にエラーメッセージも出力されないので注意が必要です。

出力リダイレクションによる上書きを制限するには

出力リダイレクションによる上書きを制限したい場合はシェルのオプションnoclobberを有効にします。変更にはsetコマンドを使います。引数無しのset -oで現在の設定を確認できます。

$ set -o | grep noclobber
noclobber	off
$ set -o noclobber
$ echo "Hello America" > hw.txt
-bash: hw.txt: cannot overwrite existing file
$ set +o noclobber (+oで元に戻す)

入力リダイレクション

ファイルを標準入力に指定すると、ファイルの内容を読み取ってコマンドなどを実行できます。

$ cat < ls.txt
total 1856
-rw-rw-r-- 1 ubuntu ubuntu 0 Dec 16 12:58 2
-rw-rw-r-- 1 ubuntu ubuntu 72 Mar 5 08:05 hello.go
-rw-rw-r-- 1 ubuntu ubuntu 59 Mar 5 07:15 index.html
-rw-rw-r-- 1 ubuntu ubuntu 0 Mar 12 08:30 ls.txt
-rwxr-xr-x 1 root root 1880089 Mar 5 08:06 myapp
drwxrwxr-x 2 ubuntu ubuntu 4096 Feb 14 04:59 share
drwxr-xr-x 3 ubuntu ubuntu 4096 Dec 14 08:39 snap

これは、「cat ls.txt」の実行で得られる結果と同じですが、内部の動きは異なります。

「cat < ls.txt」の場合はシェル(bash)がファイルを開いている(標準入力にファイルをリダイレクトしている)のに対して、「cat ls.txt」はcatコマンドがファイルを開いています。

存在しないファイルを指定して実験してみましょう。

$ cat xxx
cat: xxx: No such file or directory
$ cat < xxx
-bash: xxx: No such file or directory

前者はシェル(bash)で後者はcatと、エラーメッセージの出所が異なっているのがわかります。

標準エラー出力

標準エラー出力は、主にコマンドのエラーメッセージを出力するために使用されます。

lsコマンドで存在しないファイルを指定した場合、次のようなメッセージが表示されます。

$ ls -l xyz
ls: cannot access 'xyz': No such file or directory

これはエラーメッセージが標準エラー出力に出力されているということです。

同じコマンドをファイルにリダイレクトしてみます。

$ ls -l xyz > ls.txt
ls: cannot access 'xyz': No such file or directory
$ cat ls.txt
$ (何も表示されません)

標準出力はファイルにリダイレクトしたのですが、画面にはエラーメッセージだけが表示されます。

ls.txtの内容をcatで確認するとxyzというファイルが存在しなかったので何も表示されません。

標準エラー出力もファイルにリダイレクトできます。標準出力と区別するために、2>という記号を使います。

コマンド 2> ファイル名

先のコマンドの標準エラー出力をファイルにリダイレクトします。

$ ls -l xyz 2> err.txt
$ (何も表示されません)
$ cat err.txt
ls: cannot access 'xyz': No such file or directory

この場合、エラーメッセージはターミナル画面に表示されません。出力したファイルにエラーメッセージが保存されています。このようにコマンドの結果とエラーメッセージを分けておくことができます。

標準出力と標準エラー出力を同時にリダイレクトすることもできます。

$ ls -l abc > lsabc.txt 2> errabc.txt
$ (何も表示されません)
$ cat lsabc.txt
$ (何も表示されません)
$ cat errabc.txt
ls: cannot access 'abc': No such file or directory

標準出力と標準エラー出力をひとつのファイルにまとめてリダイレクトすることもできます。

標準出力をファイルにリダイレクトした後に、2>&1をいう記号を書きます。

$ ls -l xxx > stdouterr.txt 2>&1
$
$ cat stdouterr.txt
ls: cannot access 'xxx': No such file or directory

Linuxでは標準入出力は数値として管理されています。

冒頭にも表にしましたが、標準入力が0、標準出力が1、標準エラー出力が2です。

入出力チャネル数値
標準入力(stdin)0
標準出力(stdout)1
標準エラー出力(stderr)2

2>&1は、2の標準エラー出力を1の標準出力と同じファイルにリダイレクトするという記述になります。

リダイレクトのまとめ

記号内容
< ファイル標準入力をファイルに変更
> ファイル標準出力をファイルに変更
>> ファイル標準出力をファイルの末尾に追記
2> ファイル標準エラー出力をファイルに変更
2>> ファイル標準エラー出力をファイルの末尾に追記
> ファイル 2>&1標準出力と標準エラー出力をまとめてファイルに変更

参考にした書籍