0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Linux 標準入力 標準出力 リダイレクト

Last updated at Posted at 2025-01-25

はじめに

UNIX 哲学には

Everything is a file

という設計思想があり、これに基づき構築された Linux では 全ての入出力(I / O)操作は「ファイル」に対して行われる

デバイス操作は デバイスファイル に対して、ネットワークを介したデータのやり取りはソケットファイルに対して行われる。パイプ も内部的には仮想ファイルとして扱われているし、端末もファイルとして存在している。

Linux におけるほとんどの プロセス は究極的には以下の単純操作を行っているに過ぎない。

  • ファイルを read する
  • ファイルへ write する

具体的には、プロセスは システムコールreadwrite を内部で呼び出している。

ファイルディスクリプタ

実は、システムコールの readwrite はファイルパスに対して直接実行することはできない。

カーネルが操作できるのは「open されたファイル」だけである。

  • カーネルは「open されたファイル」を read する
  • カーネルは「open されたファイル」へ write する

ストレージ上のファイルは、カーネルの オープンファイルテーブル に登録されるまでは、プロセスの入出力対象にはならない。

オープンファイルテーブルは カーネルが保持している

ファイルをこのテーブルに登録するためには、システムコール open を使用する。

プロセスから open が呼び出されると、カーネルは引数として渡されたファイルパスを名前解決した後、ストレージ上のファイルをオープンファイルテーブルに登録し、最後に ファイルディスクリプタ という整数値を呼び出し元のプロセスに返す。

LPIC (17).png

各プロセスはカーネルに割り当てられたファイルディスクリプタを ファイルディスクリプタテーブル に保持する。ファイルディスクリプタは、プロセスが呼び出す readwrite の引数として使用される。

ファイルディスクリプタテーブルは プロセスが保持している

open は、いわば「ファイル利用の登録申請」であり、readwrite はファイルディスクリプタを利用して「どのファイルに対して操作するか」をカーネルに伝える。

ストリーム

前述の通り、プロセスはファイルディスクリプタを使用して、「open されたファイル」への read / write を行う。

open されたファイル」を read すると、データは連続的(シーケンシャル)なバイナリとして取得される。この時、取得される連続的なデータを ストリーム と表現する。

ストリームは抽象的概念であり、実態はカーネルが readwrite で使う「バッファ」である。

ストリームは、プロセスがファイルを read することによって生成され、別の(あるいは同じ)ファイルに write され、最後は close されることによって消滅するというライフサイクルを持っている。

LPIC (16).png

ファイルとプロセスはストリームを介して 接続されている と表現する。

ストリームの接続

通常、プロセスが使用する入出力ストリームは明示的に指定されないことが多い。

例えば $ ls コマンドを実行するとき、ls プロセスの実行結果をどこへ出力するかは指定されていない。

出力先を指定していない
$ ls
a.txt    b.txt    c.txt    ...

出力先が指定していないにも関わらず、ls の実行結果は 端末/dev/tty)に出力されている。

このこからも分かるように、プロセスに対して明示的に入出力先を指定しなくても良いのは、プロセスにはデフォルトで割り当てられている入出力ストリームが存在する ためである。

この「プロセスに対してデフォルトで割り当てられている入出力ストリーム」を標準入力、標準出力、標準エラー出力と呼ぶ。

全てのプロセスは、標準入力、標準出力、標準エラー出力と呼ばれるストリームを保持している

LPIC (14).png

とても大事なので、もう一度。

全てのプロセスは、標準入力、標準出力、標準エラー出力と呼ばれるストリームを保持している

具体的には、プロセスが起動される時、各プロセスが持つ標準入出力に対して常に 012 のファイルディスクリプタが自動的に割り当てられる。

ファイルディスクリプタ ストリーム
0 標準入力
1 標準出力
2 標準エラー出力

ただし、これらのストリームが「何に接続されるか」は状況に依存する(端末、パイプ、ファイルなど)。

ファイルディスクリプタ ストリーム 接続先
0 標準入力 状況に依存
1 標準出力 状況に依存
2 標準エラー出力 状況に依存

各プロセスに関連するファイルディスクリプタは、/proc/プロセスID/fd/ ディレクトリに 仮想ファイル として格納され、端末への シンボリックリンク として表示される。

標準入出力の接続先を確認する
$ ls -l /proc/$$/fd
lrwx------ 1 user user 64 Feb 15 12:34 0 -> /dev/tty1
lrwx------ 1 user user 64 Feb 15 12:34 1 -> /dev/tty1
lrwx------ 1 user user 64 Feb 15 12:34 2 -> /dev/tty1

リダイレクト

各プロセスが保持するストリームは、接続先を変更することができる。

このストリームの接続先変更を リダイレクト と呼び、これによって、コマンド同士を連携させ、様々なスクリプトを実現することができる。

LPIC (18).png

標準入力 / stdin

Standard Input

プロセスに対して明示的に入力元ストリームを指定しなかった場合に使用される、標準のデータ入力元ストリーム。

ファイルディスクリプタは 0

通常、多くのプロセスの標準入力は キーボード/dev/ttyX)に接続されている。

標準出力 / stdout

Standard Output

プロセスに対して明示的に出力先ストリームを指定しない場合に標準で使用される、データの出力先ストリーム。

ファイルディスクリプタは 1

多くのプロセスは通常、標準出力が 端末/dev/ttyX) に接続されている。

$ ls$ echo$ printf の標準出力は /dev/ttyX に接続されている。

標準エラー出力 / stderr

Standard Error

プロセスに対して明示的にエラーメッセージの出力先ストリームを指定しなかった場合に標準で使用される、エラーメッセージや警告メッセージの出力先ストリーム。

ファイルディスクリプタは 2

通常、多くのプロセスの標準エラー出力は 端末/dev/ttyX) に接続されている。

ファイルディスクリプタ

File Descriptor

リソースを識別するための識別子

リソースへアクセスするためのインターフェースとしてカーネルから提供される。

「リソース」とは下記のようなものを指す。

  • ファイル
  • 標準入出力
  • パイプ
    • プロセス間でデータをやり取りするための特殊なリソース
    • ファイルディスクリプタを使って実装されている
  • ソケット
    • TCP / UDP 通信はシステムコールsocket() で作成するファイルディスクリプタを使って実装されている

ファイルディスクリプタはリソースの識別子であるものの、全てのリソースに対して存在するわけではなく、プロセスがリソースを open した時に初めてそのプロセスに対して割り振られる

具体的には、プロセスがシステムコール の open()socket() によって新たにファイルやデバイスを開くときに、戻り値としてファイルディスクリプタが返却され、これがそのプロセスに対して割り当てられる。その後の read()write() などのリソースとのやり取りはこのファイルディスクリプタを使って行われる。

プロセスは下記のようなテーブルを プロセスごとに保持 していて、ファイル、ソケット、パイプなどのリソースへアクセスを行う。

標準入出力に対しては、あらかじめ 012 の固定値が割り当てられている。

ファイルディスクリプタ リソース デフォルトの接続先
0 標準入力 キーボード(/dev/ttyX
1 標準出力 端末(/dev/ttyX
2 標準エラー出力 端末(/dev/ttyX
3 以降 プロセスがオープンしたファイル・ソケット・パイプなど -

各プロセスに関連するファイルディスクリプタは、/proc/プロセスID/fd/ ディレクトリに 仮想ファイル として格納され、端末への シンボリックリンク として表示される。

例えば、

0 -> /dev/tty1

なら、標準入力(0)が tty1 に接続されていることを示す。

ファイルディスクリプタを確認する(コンソールTTY)
$ ls -l /proc/$$/fd
lrwx------ 1 user user 64 Feb 15 12:34 0 -> /dev/tty1
lrwx------ 1 user user 64 Feb 15 12:34 1 -> /dev/tty1
lrwx------ 1 user user 64 Feb 15 12:34 2 -> /dev/tty1
lr-x------ 1 user user 64 Feb 15 12:34 255 -> /dev/tty1

$$ビルトインシェル変数 としても利用される表現で、現在実行中のプロセスを表す。
/proc/ ディレクトリについては こちら

特定のプロセスのファイルディスクリプタを確認する
$ ls -l /proc/12345/fd  # PID が 12345 のファイルディスクリプタを確認

$ tty

teletypewriter = tele(遠隔地の)タイプライター

現在の環境のシェルの

  • 標準入力
  • 標準出力
  • 標準エラー出力

が接続された デバイスファイル を表示するコマンド。

標準出力先を表示する
$ tty

出力される /dev/ttys000/dev/ttys001 など tty で始まるものは一般に、ローカル環境の端末を表し、pts で始まるものは SSH や Telnet などを経由してネットワーク越しに接続された端末を表す。

(端末については こちら

パイプ / |

Pipeline

コマンド同士を | で連結することにより、コマンドの標準出力を、別のコマンドの標準入力と接続することができる。

|
$ コマンド1 | コマンド2
ls が生成したストリームを grep が受け取る
$ ls | grep ".txt"

リダイレクト

リダイレクト演算子を使用して標準入力や標準出力を変更すること。

ファイルディスクリプタ を組み合わせて使用するものがある。

>

標準出力を、演算子の右側で指定したファイルに変更することができる。

ファイルが存在することが前提で、出力結果は上書きされる。

実行結果を標準出力へ出力するコマンド(echoprintfpwddate、...)に対して使用することができる。

>
$ コマンド > ファイル

ファイルディスクリプタを指定すると、標準エラー出力をファイルに変更することができる。

2>
$ コマンド 2> ファイル

>>

標準出力を、演算子の右側で指定したファイルに変更することができる。

ファイルが存在することが前提で、出力結果は末尾に追記される。

>>
$ コマンド >> ファイル名

ファイルディスクリプタを指定すると、標準エラー出力をファイルに変更することができる。

2>>
$ コマンド 2>> ファイル

<

標準入力を、演算子の左側で指定したファイルに変更することができる。

標準入力を使用するコマンド(catgrepsortuniqwccutteeheadtail)に対して使用することができる。

<
$ コマンド < ファイル

<<

ヒアドキュメント(Here Document)を標準入力とするための機能。

<< で指定した任意の文字列を「ヒアドキュメント開始」、「ヒアドキュメントの終了」の合図とすることができる。

文字列には End of File の頭文字をとった EOF がよく使用される。

ヒアドキュメント
$ コマンド << EOF
# ヒアドキュメントとなる部分
# ヒアドキュメントとなる部分
# ヒアドキュメントとなる部分
EOF
終了の合図は何でも良い
$ コマンド << AAA
# ヒアドキュメントとなる部分
# ヒアドキュメントとなる部分
# ヒアドキュメントとなる部分
AAA

<< はシェルに対して「次に続く文字列(EOF)が現れるまでを標準入力として扱う」という指示を与えている。

またヒアドキュメント内では、変数展開コマンド置換 を使用することができる。

&>

& を使うことで、標準出力と標準エラー出力を同じ先にリダイレクトできる。

POSIX には準拠しておらず Bash 独自の構文となっている。2>&1 と同じ。

&>
$ コマンド &> ファイル

>&

& を使うことで、ファイルディスクリプタを複製することができる。

POSIX に準拠した構文。

ファイルディスクリプタ m を n に複製する
$ コマンド n>&m

2>&1

標準出力を標準エラー出力に複製する。

結果的に、標準出力と標準エラー出力の両方を同じ場所にリダイレクトさせることができる。

2>&1
$ コマンド > ファイル名 2>&1

2>&1 が末尾につく理由

2>&1 はあくまでも「標準エラー出力」を「現在の標準出力」に合わせる効果しかない。

2>&1
$ コマンド > ファイル名 2>&1

上記では、 $ コマンド > ファイル名 によって標準出力が指定したファイルに変更された後に、標準エラー出力を標準出力先に統一している。

誤って、

誤り
$ コマンド 2>&1 ファイル名 

とした場合、そもそも標準出力先を変更していないので、何も変化がない。

また、

誤り
$ コマンド 2>&1 > ファイル名 

とした場合も、エラー標準出力が変更されるタイミング($ コマンド 2>&1)の標準出力は、まだリダイレクトによって変更されていないので、$ コマンド 2>&1 が何も効果を持たない。

/dev/null

特殊な デバイスファイル

/dev/null に送られたデータは、即座に消去され、どこにも保存されない。

保存されないため、ストリームに容量制限がなく、ストレージを占有することがない。

$ cat

concatenate

ファイルの内容を標準出力へ出力する。

$ cat
$ cat ファイル名
$ cat -n ファイル名 # 行番号も表示する

$ xargs

標準入力から受け取った文字列を、別のコマンドの引数に渡しながら実行する。

パイプ と併せて利用される。

$ xargs
$ コマンド | xargs 実行したいコマンド # 実行したいコマンドには、先頭のコマンド実行結果が標準入力として渡る

-n オプションを指定すると、引数を指定した数ずつ分割して渡すことができる。

$ xargs -n
$ echo "arg1 arg2 arg3" | xargs -n 1 echo

$ tee

標準入力から受け取ったストリームを、標準出力とファイルの両方へ出力する。

コマンドの実行結果をファイルに書き込みしながら、次のコマンドへと渡すことができる。

パイプ と併せて利用される。

Untitled presentation.png

$ tee
$ コマンド | tee ファイル名 | コマンド # 最後のコマンドには、最初のコマンドの実行結果が標準入力として渡る

$ dd

Data Duplicator(俗称)
Data Definition(正式)

入力ファイルからデータをブロック単位で読み取り、ファイルまたは標準出力へ送信する。

ファイルシステムの構造やデバイスのブロック単位でコピーできるため、ディスクのバックアップやデータの変換に利用される。

$ cp よりも、より低レベルなデータ操作が可能。

if = input file
of = output file

ifof にはファイルだけでなくデバイス (/dev/sda など) を指定することも可能なため、ハードディスクや仮想デバイスの操作にも使用される。

丸ごとコピーする
$ dd if=入力ファイル of=出力ファイル

$ read

標準入力からデータを読み取り、変数に格納する。

標準入力からデータを変数に格納する
$ read 格納先変数名

変数を指定しない場合、デフォルトでは ビルトインシェル変数$REPLY に格納される。

格納先を指定しない場合 $REPLY に格納される
$ read
HELLO
$ echo $REPLY
HELLO
タイムアウト時間を指定する(秒単位)
$ read -t 秒数 格納先変数名
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?