トップ > Linuxらしい使い方を覚える >
リダイレクトで標準入力と標準出力をファイルへつなぐ

  

リダイレクトで標準入力と標準出力の結びつけを切り替える

ここからはファイルやディレクトリの話題からは離れ、シェルの機能を中心に解説していきます。 この記事では、シェルの機能である "リダイレクト" について解説します。 リダイレクトとは、標準入力や標準出力をファイルへ結びつけるための機能です。

また、この記事では "UNIX系OSでは何でもファイルとして扱う" ということを実感してもらうことも目指しています。

シェルについてのおさらい

端末の開き方と閉じ方の記事で解説したように、端末での作業であなたの入力を待っているのはシェルと呼ばれるソフトウェアです。 端末はあくまでも窓口であり、窓口の先に控えている担当者がシェルだと思ってください。

そしてシェルは、標準入力からコマンドを受け取り、結果を標準出力へ書き込んでいます。 標準入力はキーボードにつながっており、標準出力が画面につながっています。

リダイレクトとは、標準入力や標準出力の結びつきを変更してファイルにつなげるための機能です。

リダイレクトを使ってみよう

では、リダイレクトの機能を使ってみましょう。 作業はX Window Systemではなくコンソールで行いますCTRL+ALT+F1キーを押してください。

以下のようにコンソールに切り替わります。

Linux Mint 21.3 Virginia myhostname tty1

myhostname login:

右上に "tty1" と表示されていることからわかるように、1番目のコンソールです

では、ログインしましょう。 ユーザIDとパスワードを入力してください。

  
ユーザIDとは、インストール時に『あなたの情報を入力してください』という画面で入力したユーザー名です。
  
ユーザIDの入力後にEnterキーを押すことで、パスワードを入力できるようになります。 パスワードを入力したら、さらにEnterキーを押してください。

以下のようにログインに成功するとシェルプロンプトが表示されます。

taro@myhostname:~$

ではここで画面にテキストを表示してみましょう。 画面にテキストを表示するためのコマンドは echo コマンドです。 キーボードから echo Hello と入力して、Enterキーを押してください。


echo Hello

 

以下のように画面に "Hello" と表示されます。

taro@myhostname:~$ echo Hello
Hello
taro@myhostname:~$

このように echo コマンドを利用することで画面にテキストを表示することができます。

  
正確には、"画面に" ではなく "標準出力に" です。

では、いよいよリダイレクトの登場です。 キーボードから echo Hello > testfile と入力して、Enterキーを押してください。


echo Hello > testfile

 

以下のように画面には何も表示されません。

taro@myhostname:~$ echo Hello > testfile
taro@myhostname:~$

このように "echo Hello" の後ろに " > testfile" を付け足したところ、画面には何も表示されなくなりました。

ではここで、ディレクトリの内容を表示してみましょう。 キーボードから ls -l と入力して、Enterキーを押してください。


ls -l

 

以下のようにカレントディレクトリの内容が表示されます。 "testfile" という名前のファイルが新たに作られていることがわかります。

taro@myhostname:~$ ls -l
合計 36
drwxr-xr-x 2 taro taro 4096  6月 10 13:11 Desktop
drwxr-xr-x 2 taro taro 4096  6月 10 13:11 Documents
drwxr-xr-x 2 taro taro 4096  6月 10 13:11 Downloads
drwxr-xr-x 2 taro taro 4096  6月 10 13:11 Music
drwxr-xr-x 2 taro taro 4096  6月 10 13:11 Pictures
drwxr-xr-x 2 taro taro 4096  6月 10 13:11 Public
drwxr-xr-x 2 taro taro 4096  6月 10 13:11 Templates
drwxr-xr-x 2 taro taro 4096  6月 10 13:11 Videos
-rw-rw-r-- 1 taro taro    6  6月 25 20:04 testfile
taro@myhostname:~$

画面には何も表示されず、カレントディレクトリにファイルが作成されている、ということがわかりました。 どうやら、画面へ出力される代わりにファイルへ出力されたようです。

では、ファイル testfile の内容を確認してみましょう。 cat コマンドでファイルの内容を表示させることができます。 キーボードから cat testfile と入力して、Enterキーを押してください。


cat testfile

 

以下のように testfile の内容が画面に表示されます。 ファイルの内容が "Hello" であることがわかります。

taro@myhostname:~$ cat testfile
Hello
taro@myhostname:~$

思った通り、画面へ出力される代わりにファイルへ出力されていました。 このようにコマンドの後ろに > を付けることで、出力結果を画面ではなくファイルへ保存することができます。 これがリダイレクトの機能です。


続いては、標準入力のリダイレクトについて紹介します。 キーボードから入力する代わりにファイルの内容を割り当てる、という機能です。

ここでは電卓の機能である bc コマンドを例に紹介することにします。 なお、まだリダイレクトの機能は使いません。

では、キーボードから bc と入力して、Enterキーを押してください。


bc

 

以下のように入力待ちの状態になります。

taro@myhostname:~$ bc
bc 1.07.1
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
 

bc コマンドは対話型のコマンドであり、あなたが命令を入力するのを待っています。 では、1 + 2 を計算させてみましょう

キーボードから 1 + 2 と入力して、Enterキーを押してください。


1 + 2

 

以下のように計算結果の 3 が表示され、引き続き入力待ちの状態になります。

taro@myhostname:~$ bc
bc 1.07.1
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
1 + 2
3
 

では bc コマンドを終了します。 キーボードから quit と入力して、Enterキーを押してください。


quit

 

以下のように bc コマンドが終了し、シェルプロンプトに戻ります。

taro@myhostname:~$ bc
bc 1.07.1
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
1 + 2
3
quit
taro@myhostname:~$

このように、bc コマンドは quit と入力することで終了することができます。 なお、キーボードからCTRL+Dキーを入力することでも終了させることができます。

それでは、本題の標準入力のリダイレクトを使ってみましょう。 計算式 "10 - 3" をファイルに保存し、bc コマンドにはそのファイルを読み込ませます。

まずは、計算式をファイルに保存します。 キーボードから echo 10 - 3 > testfile と入力して、Enterキーを押してください。


echo 10 - 3 > testfile

 

以下のように画面には何も表示されません。 理由はもちろん、出力内容がファイル testfile にリダイレクトされたためです。

taro@myhostname:~$ echo Hello > testfile
taro@myhostname:~$

なお、先ほどのコマンドで書き込まれた "Hello" は上書きされて消えています。

では、このファイル testfile を bc への入力として読み込ませてみます。 キーボードから bc < testfile と入力して、Enterキーを押してください。


bc < testfile

 

以下のように 10 - 3 の計算結果の 7 が表示されます。

taro@myhostname:~$ bc < testfile
7
taro@myhostname:~$

このようにコマンドの後ろに < を付けることで、ファイルの内容を標準入力に割り当てることができます。

標準出力のリダイレクトは追記することもできる

標準出力をリダイレクトすると、リダイレクト先のファイルの内容は上書きされます。 例えば、

echo Hello > filename

と実行すると、ファイル filename の内容は "Hello" となり、元々の内容は消えてなくなります。

上書きではなく、追記することもできます。 追記する場合には、

echo Hello >> filename

のように ">>" と記述します。 なお、ファイル testfile が存在しない場合は新規にファイルが作成されます。 つまり、上書きと同じ結果になります。 ファイルが存在しないからといってエラーになる心配はありません。

UNIX系OSは何でもファイルとして扱うということを実感してみよう

覚えたリダイレクトの機能を使い『UNIX系OSは何でもファイルとして扱う』ということを体験してみましょう。 作業は引き続きコンソールで行いますが、もう1つ端末が必要ですCTRL+ALT+F2キーを押してください。

以下のように新たなコンソールに切り替わります。

Linux Mint 21.3 Virginia myhostname tty2

myhostname login:

右上に "tty2" と表示されていることからわかるように、2番目のコンソールに切り替わりました。 では、ログインしてください。

以下のようにログインに成功するとシェルプロンプトが表示されます。

taro@myhostname:~$

では、"Hello" というテキストを出力し、1番目のコンソールである tty1 にリダイレクトしてみましょう。 キーボードから echo Hello > /dev/tty1 と入力して、Enterキーを押してください。


echo Hello > /dev/tty1

 

以下のように画面には何も表示されません。

taro@myhostname:~$ echo Hello > /dev/tty1
taro@myhostname:~$

理由はもちろん、画面へ出力される代わりにファイル /dev/tty1 へ出力されたためです。 では、ファイル /dev/tty1 とは何なのでしょうか。

ファイル /dev/tty1 は、1番目のコンソールである tty1 の入出力を司るファイルです。 つまり、コンソール tty1 の標準入力であり標準出力であるモノの正体が /dev/tty1 というわけです。

ではここで、コンソール tty1 に切り替えてみましょう。 CTRL+ALT+F1キーを押してください。

以下のようにコンソール tty1 に切り替わります。 画面には "Hello" と表示されていることがわかります。

taro@myhostname:~$ Hello
 

このように2番目のコンソール tty2 から 1番目のコンソール tty1 の画面にテキストを書き込むことができました。 これが、何でもファイルとして扱うというUNIX系OSの設計だからこそできる操作です。

デバイスファイルのファイルパスを表示するには

上で説明したように、コンソール tty1 の標準入力・標準出力の正体が /dev/tty1 というファイルです。 コンソール tty1 から見れば、/dev/tty1 は仮想的なキーボードであり仮想的な画面でもあるということになります。

そのような "機器と対応しているファイル" のことを "デバイスファイル" と呼びます。 キーボードや画面だけでなく、ハードディスクやUSBフラッシュメモリに対応したデバイスファイルももちろん存在しています。

では、端末に対応するデバイスファイルのファイルパスはどうやって知ればいいのでしょうか。 なお、ファイルパスとは /dev/tty1 のようにディレクトリパス + ファイル名のテキストのことです。

端末のデバイスファイルは、tty コマンドで知ることができます。 試しに実行してみましょう。 キーボードから tty と入力して、Enterキーを押してください。


tty

 

以下のようにデバイスファイルのファイルパスが表示されます。

taro@myhostname:~$ tty
/dev/tty1
taro@myhostname:~$

このように tty コマンドを使うことで、端末のデバイスファイルを知ることができます。

  

リダイレクトに関する補足 - 標準エラー出力について -

リダイレクトに関して、まだ説明できていない重要なことがあります。 ここではそれについて解説しておきます。

説明できていない重要なこと、というのは "標準エラー出力" というファイルについてです。

すでに説明したように、UNIX系OSには標準入力と標準出力というファイルがあります。 標準入力はキーボードにつながるファイルで、標準出力が画面につながるファイルです。

シェルや各コマンドは標準入力から入力内容を受け取り、結果を標準出力に返しています。 ...と、何度も説明してきましたが、実はもう1つ "標準エラー出力" というファイルも存在していたのです。

では、標準エラー出力はどこにつながっているのでしょうか。 答えは "画面" です。 つまり、標準出力と同じです。 標準出力へ書き込んでも、標準エラー出力へ書き込んでも、どちらにしても結局は画面に表示されるのです。

なお、標準エラー出力に書き込まれるのはエラーメッセージなどです。 シェルや各コマンドは、エラーメッセージなどは標準エラー出力に書き込み、それ以外の結果を標準出力に書き込みます。

試しに ls コマンドにエラーを発生させて、動きを見てみましょう。 端末を開き、キーボードから ls Documents NotFound と入力して、Enterキーを押してください。


ls Documents NotFound

 
  
Documents は存在しますが、NotFound は存在しません。

以下のように "NotFound は見つからない" というエラーメッセージと、Documents ディレクトリ内容の情報が同時に表示されます

taro@myhostname:~$ ls Documents NotFound
ls: 'NotFound' にアクセスできません: そのようなファイルやディレクトリはありません
Documents:
対象のファイル.txt

ではここで、出力内容をリダイレクトでファイルへ保存してみましょう。

キーボードから ls Documents NotFound > stdout と入力して、Enterキーを押してください。


ls Documents NotFound > stdout

 

以下のようにDocuments ディレクトリの内容の情報は見当たらなくなりました。 ただし、エラーメッセージは引き続き画面に表示されています

taro@myhostname:~$ ls Documents NotFound > stdout
ls: 'NotFound' にアクセスできません: そのようなファイルやディレクトリはありません
taro@myhostname:~$

では、ファイル stdout の内容を見てみましょう。 キーボードから cat stdout と入力して、Enterキーを押してください。


cat stdout

 

以下のようにDocuments ディレクトリの内容の情報が書き込まれていたことが確認できます。 エラーメッセージは見当たりません。

taro@myhostname:~$ cat stdout
Documents:
対象のファイル.txt

このように > によるリダイレクトは標準出力のみに作用します。 標準エラー出力には効果はありません。

では、標準エラー出力の内容をリダイレクトするにはどうすればいいのでしょうか。 それは > ではなく 2> と記述すればいいだけです。

  
ファイルの読み書きには "ファイルディスクリプタ" と呼ばれる内部的な番号が利用されます。 標準入力のファイルディスクリプタは 0(ゼロ) で、標準出力のファイルディスクリプタは 1 、標準エラー出力のファイルディスクリプタは 2 です。

では、キーボードから ls Documents NotFound > stdout 2> stderr と入力して、Enterキーを押してください。


ls Documents NotFound > stdout 2> stderr

 
  
リダイレクト先のファイルはそれぞれ異なります。 stdout と stderr の2つのファイルです。

以下のようにDocuments ディレクトリの内容の情報もエラーメッセージも、どちらとも表示されなくなりました。

taro@myhostname:~$ ls Documents NotFound > stdout 2> stderr
taro@myhostname:~$

では、それぞれのファイルの内容を表示してみましょう。 まずは、stdout ファイルからです。 キーボードから cat stdout と入力して、Enterキーを押してください。


cat stdout

 

以下のようにDocuments ディレクトリの内容の情報が書き込まれています。

taro@myhostname:~$ cat stdout
Documents:
対象のファイル.txt

続いては、stderr ファイルです。 キーボードから cat stderr と入力して、Enterキーを押してください。


cat stderr

 

以下のようにエラーメッセージが書き込まれていることがわかります。

taro@myhostname:~$ cat stderr
ls: 'NotFound' にアクセスできません: そのようなファイルやディレクトリはありません
taro@myhostname:~$

このように 2> filename と記述することで、標準エラー出力の内容をファイル filename へ保存することができます。


上で解説したように、標準出力と標準エラー出力の両方ともをリダイレクトすることは難しくはありません。

ただしそれは、標準出力と標準エラー出力を別ファイルへリダイレクトする場合に限ってのことです。 標準出力と標準エラー出力を同じファイルへリダイレクトしようとすると、話は変わってきます。

実際にやってみましょう。 キーボードから ls Documents NotFound > filename 2> filename と入力して、Enterキーを押してください。


ls Documents NotFound > filename 2> filename

 
  
リダイレクト先のファイルは1つです。 同じファイルに標準出力と標準エラー出力の両方をリダイレクトしています。

以下のようにDocuments ディレクトリの内容の情報もエラーメッセージも、どちらとも表示されません。

taro@myhostname:~$ ls Documents NotFound > filename 2> filename
taro@myhostname:~$

では、リダイレクトは成功したのでしょうか。 ファイルの内容を見てみましょう。

キーボードから cat filename と入力して、Enterキーを押してください。


cat filename

 

以下のようにメッセージの一部が見当たらず、また、文字化けもしています。

taro@myhostname:~$ cat filename
Documents:
対象のファイル.txt
��せん: そのようなファイルやディレクトリはありません
taro@myhostname:~$

このように標準出力と標準エラー出力を同じファイルへリダイレクトすると正常には動作しません。 理由は、ファイルへの書き込みが競合するためです

では、どうすればいいのでしょうか。 答えは単純で、ファイルへ書き込まれる前に標準出力と標準エラー出力を混ぜてしまえばいいのです

では、実際にやってみましょう。 キーボードから ls Documents NotFound > filename 2>&1 と入力して、Enterキーを押してください。


ls Documents NotFound > filename 2>&1

 
  
2>&1 が新たに追加した部分です。

以下のようにDocuments ディレクトリの内容の情報もエラーメッセージも表示されません。

taro@myhostname:~$ ls Documents NotFound > filename 2>&1
taro@myhostname:~$

では、ファイルの内容を確認してみましょう。

キーボードから cat filename と入力して、Enterキーを押してください。


cat filename

 

以下のように正常に書き込まれており、メッセージの欠けも文字化けもありません。 リダイレクトしない時と同じ表示になっています。

taro@myhostname:~$ cat filename
ls: 'NotFound' にアクセスできません: そのようなファイルやディレクトリはありません
Documents:
対象のファイル.txt
taro@myhostname:~$

このように 2>&1 を付け足すことで、標準出力と標準エラー出力を同じファイルへリダイレクトすることができます。 なお、

2>&1

は、ファイルディスクリプタ 2 を ファイルディスクリプタ 1 へ流し込む、という意味です。 つまり、標準エラー出力を標準出力へ流し込むということです。

標準エラー出力が標準出力へ流し込まれ、その後に標準出力がファイルへリダイレクトされました。 そのため、ファイルへの書き込みの競合は発生せずに済んだのです。

なお、記述の順序を変えて、

ls Documents NotFound 2>&1 > filename

としてしまうと、正常には動作しません。 エラーメッセージがリダイレクトされずに画面に表示されてしまいます。

標準出力と標準エラー出力を同じファイルへリダイレクトするには、

ls Documents NotFound > filename 2>&1

と記述しなければなりません。

では、間違った記述である、

ls Documents NotFound 2>&1 > filename

は、何がダメなんでしょうか。 それは、リダイレクトの記述はコマンドラインの後ろから前に向かって解釈されるからです。

上記の例だと、標準出力への出力内容が filename ファイルへ出力された後に、標準エラー出力への出力内容が標準出力へ出力されます。 ということは、

ls Documents NotFound > filename

と同じ結果にしかならないということです。

順序について少し詳しく

正確には、シェルはリダイレクトの記述を後ろから解釈するわけではなく、ちゃんと前から解釈してくれます。 ではなぜ、

ls Documents NotFound 2>&1 > filename

としてはダメなか、もう少し詳しく説明します。 コマンドを入力するとシェルはどのように解釈しているかを見てみましょう。

コマンドが入力されるとシェルは最初に、

  1. 標準入力をキーボードに対応させる
  2. 標準出力を画面に対応させる
  3. 標準エラー出力を画面に対応させる

という関連付けを行います。 ここで大切なことは、標準入力とキーボード / 標準出力と画面 / 標準エラー出力と画面はまだ接続されていない、ということです。

キーボードが紐で標準入力とつながっている様子や標準出力が紐で画面につながっている様子は想像しないでくださいそういう状態ではありません

想像すべきは、"標準入力" というラベルが貼られた箱があり、そこに "キーボード" と書かれた紙が入っている様子です。 同じく、"標準出力" というラベルの箱には "画面" と書かれた紙が、"標準エラー出力" というラベルの箱にも "画面" と書かれた紙が入っています。

箱のラベル 箱の中身
標準入力 "キーボード" と書かれた紙
標準出力 "画面" と書かれた紙
標準エラー出力 "画面" と書かれた紙

続いてシェルは、

ls Documents NotFound

の部分を解釈します。 この部分は実行するコマンドであり、リダイレクトには関係ありません。

次にシェルは、

2>&1 > filename

の部分を解釈します。 そして『標準エラー出力の出力先を標準出力と同じにせよ』という命令だと理解します。

そのように理解したシェルは、まず "標準エラー出力" のラベルの箱に入っている "画面" という紙を捨てます。 続いて "標準出力" のラベルの箱にある "画面" という紙を複製し、"標準エラー出力" のラベルの箱に入れます。 また、その紙に注意書きで "上書きではなく追加" と書き加えます。

箱のラベル 箱の中身
標準入力 "キーボード" と書かれた紙
標準出力 "画面" と書かれた紙
標準エラー出力 "画面" と書かれた紙
※"上書きではなく追加" と注意書きあり

最後にシェルは、

> filename

の部分を解釈します。 そして "標準出力" のラベルの箱の紙を "ファイル filename" と書かれた新しい紙で入れ替えます。

箱のラベル 箱の中身
標準入力 "キーボード" と書かれた紙
標準出力 "ファイル filename" と書かれた紙
標準エラー出力 "画面" と書かれた紙
※"上書きではなく追加" と注意書きあり

シェルはコマンドの解析を終えました。 "標準エラー出力" というラベルの箱には何と書かれた紙が入っているでしょうか。

そうです、紙には "画面" と書かれています。 結果、標準エラー出力は画面に出力されるのです。


なお、難しいことは考えずにリダイレクトの記述はコマンドラインの後ろから前に向かって解釈されると覚えておけばいいでしょう。 間違っていますが、覚えやすいですから。

便利な記述もある

もっと簡単な記述もあります。 以下のように、

ls Documents NotFound >& filename

と記述することで、

ls Documents NotFound > filename 2>&1

と同じ結果を得ることができます。

  

まとめ

echo コマンドで、テキストを標準出力に出力することができます。 また cat コマンドでは、ファイルの内容を標準出力に出力することができます。

コマンドの後ろに、

echo Hello > filename

のように記述することで、標準出力への出力内容を画面ではなくファイル filename へ保存することができます。 ファイル filename が存在しなければ新たに作成されます。 ファイル filename が存在している場合は内容が上書きされます。

コマンドの後ろに、

echo Hello >> filename

と記述すると、上書きではなく追記になります。 なお、ファイル filename が存在しない場合は新たに作成されますので、上書きと同じ結果になります。

操作/コマンド 説明
echo 引数として渡されたテキストを標準出力へ出力する
cat 引数として渡されたファイルの内容を標準出力へ出力する
> filename 標準出力への出力内容を画面ではなくファイル filename へ保存する(上書き)
>> filename 標準出力への出力内容を画面ではなくファイル filename へ保存する(追記)

bc コマンドで各種計算を行うことができます。 bc コマンドは対話型のコマンドであるため標準入力からの入力を待ちますが、コマンドの後ろに、

bc < filename

のように記述することで、ファイル filename から計算式を読み込ませることができます。

操作/コマンド 説明
bc 標準入力からの計算式に従って計算を行う
< filename ファイル filename の内容を標準入力への入力とする

端末の標準入力・標準出力に対応するデバイスファイルのファイルパスは tty コマンドで知ることができます。

操作/コマンド 説明
tty 現在の端末のデバイスファイルのファイルパスを表示する

入出力ファイルには標準入力・標準出力に加えて、標準エラー出力というものもあります。 シェルや各コマンドはエラーメッセージなどを標準エラー出力に書き込みます。 なお、標準エラー出力の行き先は、標準出力と同じく画面です。

コマンドの後ろに、

echo Hello 2> filename

のように記述することで、標準エラー出力への出力内容を画面ではなくファイル filename へ保存することができます。

コマンドの後ろに、

echo Hello 2>> filename

と記述すると、上書きではなく追記になります。

なお、標準出力と標準エラー出力の両方を同じファイルへリダイレクトする場合は > filename 2>&1 と記述する必要があります。 短縮表記として >& filename と記述することもできます。

操作/コマンド 説明
2> filename 標準エラー出力への出力内容を画面ではなくファイル filename へ保存する(上書き)
2>> filename 標準エラー出力への出力内容を画面ではなくファイル filename へ保存する(追記)
> filename 2>&1
(または)
>& filename
標準出力と標準エラー出力への出力内容を合わせたものをファイル filename へ保存する
メニュー