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

# はじめに

UNIX 哲学には

***Everything is a file***

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

デバイス操作は [デバイスファイル](https://qiita.com/AsahinaKei/items/09ee7ce55b1a0be01ff0#%E3%83%87%E3%83%90%E3%82%A4%E3%82%B9%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB) に対して、ネットワークを介したデータのやり取りはソケットファイルに対して行われる。[パイプ](#パイプ) も内部的には仮想ファイルとして扱われているし、端末もファイルとして存在している。

Linux におけるほとんどの [プロセス](https://qiita.com/AsahinaKei/items/e354a3814639aeac4138#%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9) は究極的には以下の単純操作を行っているに過ぎない。

- ファイルを `read` する
- ファイルへ `write` する

具体的には、プロセスは [システムコール](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%B3%E3%83%BC%E3%83%AB) の `read`、`write` を内部で呼び出している。


## ファイルディスクリプタ

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

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

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

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

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

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

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

![LPIC (17).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069520/ca37e7f2-dc34-4811-8911-a309a49f18fa.png)

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

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

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

## ストリーム

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

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

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

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

![LPIC (16).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069520/ff900b38-2616-4f22-a46d-3b554c32497a.png)

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

## ストリームの接続

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

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

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

出力先が指定していないにも関わらず、`ls` の実行結果は [端末](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E7%AB%AF%E6%9C%AB)（`/dev/tty`）に出力されている。

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

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

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

![LPIC (14).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069520/895c878f-8503-4bc8-bcac-7b270ba19086.png)

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

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

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

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

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

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

各プロセスに関連するファイルディスクリプタは、`/proc/プロセスID/fd/` ディレクトリに [仮想ファイル](https://qiita.com/AsahinaKei/items/09ee7ce55b1a0be01ff0#%E4%BB%AE%E6%83%B3%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0) として格納され、端末への [シンボリックリンク](https://qiita.com/AsahinaKei/items/8d43c366f061a27ad670#%E3%82%B7%E3%83%B3%E3%83%9C%E3%83%AA%E3%83%83%E3%82%AF%E3%83%AA%E3%83%B3%E3%82%AF) として表示される。

```console:標準入出力の接続先を確認する
$ 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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069520/66b6ce80-2f13-4eed-b7c4-8092871329bc.png)

# 標準入力 / `stdin`
Standard Input

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

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

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

# 標準出力 / `stdout`
Standard Output

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

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

多くのプロセスは通常、標準出力が [**端末**](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E7%AB%AF%E6%9C%AB%E3%82%A8%E3%83%9F%E3%83%A5%E3%83%AC%E3%83%BC%E3%82%BF)（`/dev/ttyX`） に接続されている。

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

# 標準エラー出力 / `stderr`
Standard Error

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

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

通常、多くのプロセスの標準エラー出力は [**端末**](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E7%AB%AF%E6%9C%AB%E3%82%A8%E3%83%9F%E3%83%A5%E3%83%AC%E3%83%BC%E3%82%BF)（`/dev/ttyX`） に接続されている。

# ファイルディスクリプタ
File Descriptor

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

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

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

- ファイル
- 標準入出力
- パイプ
    - プロセス間でデータをやり取りするための特殊なリソース
    - ファイルディスクリプタを使って実装されている
- ソケット
    - [TCP](https://qiita.com/AsahinaKei/items/627f0076fecc107ac686#tcp) / [UDP](https://qiita.com/AsahinaKei/items/627f0076fecc107ac686#udp) 通信はシステムコール`socket()` で作成するファイルディスクリプタを使って実装されている

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

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

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

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

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


各プロセスに関連するファイルディスクリプタは、`/proc/プロセスID/fd/` ディレクトリに [仮想ファイル](https://qiita.com/AsahinaKei/items/09ee7ce55b1a0be01ff0#%E4%BB%AE%E6%83%B3%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0) として格納され、端末への [シンボリックリンク](https://qiita.com/AsahinaKei/items/8d43c366f061a27ad670#%E3%82%B7%E3%83%B3%E3%83%9C%E3%83%AA%E3%83%83%E3%82%AF%E3%83%AA%E3%83%B3%E3%82%AF) として表示される。

例えば、 

```
0 -> /dev/tty1
```

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


```console:ファイルディスクリプタを確認する（コンソール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
```

※ `$$` は [ビルトインシェル変数](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E3%83%93%E3%83%AB%E3%83%88%E3%82%A4%E3%83%B3%E3%82%B7%E3%82%A7%E3%83%AB%E5%A4%89%E6%95%B0) としても利用される表現で、現在実行中のプロセスを表す。
※ `/proc/` ディレクトリについては [こちら](https://qiita.com/AsahinaKei/items/09ee7ce55b1a0be01ff0#fhs)。

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

## `$ tty`
teletypewriter = tele（遠隔地の）タイプライター

現在の環境のシェルの

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

が接続された [デバイスファイル](https://qiita.com/AsahinaKei/items/09ee7ce55b1a0be01ff0#%E3%83%87%E3%83%90%E3%82%A4%E3%82%B9%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB) を表示するコマンド。

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

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

（端末については [こちら](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E4%BB%AE%E6%83%B3%E7%AB%AF%E6%9C%AB)）

# パイプ / `|`
Pipeline

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

```console:|
$ コマンド1 | コマンド2
```

```console:ls が生成したストリームを grep が受け取る
$ ls | grep ".txt"
```

# リダイレクト

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

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


## `>`

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

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

実行結果を標準出力へ出力するコマンド（`echo`、`printf`、`pwd`、`date`、...）に対して使用することができる。

```console:>
$ コマンド > ファイル名
```

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

```console:2>
$ コマンド 2> ファイル名
```

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

```console:&>
$ コマンド &> ファイル名
$ コマンド >& ファイル名
```

## `>>`

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

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

```console:>>
$ コマンド >> ファイル名
```

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

```console:2>>
$ コマンド 2>> ファイル名
```

## `<`

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

標準入力を使用するコマンド（`cat`、`grep`、`sort`、`uniq`、`wc`、`cut`、`tee`、`head`、`tail`）に対して使用することができる。

```console:<
$ コマンド < ファイル名
```

## `<<`

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

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

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

```console:ヒアドキュメント
$ コマンド << EOF
# ヒアドキュメントとなる部分
# ヒアドキュメントとなる部分
# ヒアドキュメントとなる部分
EOF
```

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

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

またヒアドキュメント内では、[変数展開](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E6%96%87%E5%AD%97%E5%88%97%E4%B8%AD%E3%81%AE%E5%A4%89%E6%95%B0%E3%81%AE%E5%B1%95%E9%96%8B) や [コマンド置換](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E7%BD%AE%E6%8F%9B) を使用することができる。

## `2>&1`

標準エラー出力を標準出力に結合する。

`>` を併せて利用することで、標準出力と標準エラー出力の両方を同じ場所にリダイレクトさせることができる。

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

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

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

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

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

誤って、

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

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

また、

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

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

# `/dev/null`

特殊な [デバイスファイル](https://qiita.com/AsahinaKei/items/09ee7ce55b1a0be01ff0#%E7%89%B9%E6%AE%8A%E3%81%AA%E6%84%8F%E5%91%B3%E3%82%92%E6%8C%81%E3%81%A4%E3%83%87%E3%83%90%E3%82%A4%E3%82%B9%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB)。

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

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


# `$ cat`
concatenate

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

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

# `$ xargs`

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

[パイプ](#パイプ--) と併せて利用される。

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

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

```console:$ xargs -n
$ echo "arg1 arg2 arg3" | xargs -n 1 echo
```

# `$ tee`

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

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

[パイプ](#パイプ--) と併せて利用される。

![Untitled presentation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069520/c14c2ddf-d0bb-180c-c73a-7d6eee62cd08.png)

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

# `$ dd`
Data Duplicator（俗称）
Data Definition（正式）

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

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

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

`if` = input file
`of` = output file

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

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

# `$ read`

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

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

変数を指定しない場合、デフォルトでは [ビルトインシェル変数](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E3%83%93%E3%83%AB%E3%83%88%E3%82%A4%E3%83%B3%E3%82%B7%E3%82%A7%E3%83%AB%E5%A4%89%E6%95%B0) の `$REPLY` に格納される。

```console:格納先を指定しない場合 $REPLY に格納される
$ read
HELLO
$ echo $REPLY
HELLO
```

```console:タイムアウト時間を指定する（秒単位）
$ read -t 秒数 格納先変数名
```