Envaderを利用して、Linuxでの標準入出力を体験してみる。

30代後半、未経験からIT業界のエンジニア転職へ挑戦している、kobakichiです。 

今回は、Linux環境における標準入出力について学んだので、その振り返りと自分なりのまとめです。

## 利用した環境
今回も、環境構築が不要でLinux環境をオンラインで体験できて学べる学習サービス、`Envader`を利用しました。

`Envader`とはなんぞや？という方のためにリンクを貼っておきますので、よかったら参考にどうぞ。

[Envader](https://envader.plus/)

[コース一覧](https://envader.plus/course)

[今回使用したシナリオ](https://envader.plus/course/1/scenario/1007)

## 最初に。。
今回EnvaderにてLinuxにおける入出力を体験して学びましたが、正直標準入出力の3つについて深いところまではまだ理解できていません。

ここの理解はかなり難しいなと言うのが私の感想です。

かなり深い所まで理解したい方は、@angel_p_57さんの[標準入力・標準出力ってなに?](https://qiita.com/angel_p_57/items/03582181e9f7a69f8168)やその他の記事などを読むことで深いところまで学ぶ事ができると思います。

以上のことを踏まえて、今回学んだ事の振り返りを記事にしています。


## 標準入出力

Linuxには、`標準入力`、`標準出力`、`標準エラー出力`、この3つの考え方が存在します。最初にこの表現を見たときは、え？出力って二つあるの？一つじゃないの？と不思議に思いました。

ですが、本当にこの3つの考え方、表現があるようです。

>それぞれのコマンドは一つのプログラムです。Linuxのプログラムには、「1つの入り口と2つの出口」があります。それを、それぞれ標準入力、標準出力、標準エラー出力と言います。
>"Linux標準教科書より引用"


以下LinuCの教科書より引用
>Linuxでは、通常のファイルと同等に、ディスプレイへの出力、キーボードからの入力を扱う事ができます。キーボードからの入力もファイルの読み込みも同等に扱い、ディスプレイへの出力もファイルへの書き出しも同等に扱います。

ここに出てくるファイルの表現がなかなか理解できなかったのですが、別の記事にてこのような解説を見て、少しだけ理解できました。

>一般に入力・出力というとそれなりに広い概念です。が、ここで扱っているのはファイルとしての入力・出力、つまりファイルからデータを読み込む、ファイルにデータを書き込むといった操作やデータの流れを指します。
「あれ? でもキーボードから打ち込んで…というのもファイルなのか?」と違和感を感じられるかも知れません。が、これはある意味Linux ( UNIX系OS ) の流儀です。通常のファイルも含め、バイト列としてデータを読み込んだり書き込んだり、どちらかができるものをファイルとして扱うという決めごとなのです。このファイルの中には、ネットワーク通信を行うための、ソケットと呼ばれるモノすら含まれます。
[標準入力・標準出力ってなに?](https://qiita.com/angel_p_57/items/03582181e9f7a69f8168)より引用

***通常のファイルも含め、バイト列としてデータを読み込んだり書き込んだり、どちらかができるものをファイルとして扱うという決めごと***と解説されています。



さらに、何やら***ファイルディスクリプタ***と呼ばれる考え方もあるようで、
`プログラムが扱う操作の対象とするファイル`を`OSが識別するための番号`も存在するとのこと。
`ファイル識別子`とも呼ばれるようですね。

具体的には下記のようになっています。

| 番号(ファイルディスクリプタ) | 入出力名 | デフォルト |
|:-:|:-:|:-:|
| 0  | 標準入力  | キーボード  |
| 1  |  標準出力 | 画面(端末)  |
| 2 | 標準エラー出力 | 画面(端末) |

上記3つが標準的に付けられている番号で、この3つはプログラムが生成されると必ず作られるみたいです。

これ以外にも、他の出力先に割り当てられる番号として存在するそうです。

それでは、実際にEnvaderを利用しながら、この3つがどんなものなのかを体験してみたいと思います。

## 標準入力(stdin)
標準入力は、プログラムに入ってくるデータのことを示し、`入力ストリーム`と表現されます。標準入力先はキーボードがデフォルトになっています。

ただ、単なるキーボードの入力だけが標準入力ではなさそうで、ここの概念的なものの理解も私にはかなり難解だったのですが、

>標準入力 … 加工対象のデータの入力元や、ユーザからのパラメータ入力受付用 ( cpでの上書き確認のy/n等 )
[標準入力・標準出力ってなに?](https://qiita.com/angel_p_57/items/03582181e9f7a69f8168)より引用

>プログラムに入ってくるデータや入力元のデバイスのことです。ほとんどの場合、キーボードが標準入力先になります。
Envaderより引用

とのことで、何か入力した事、入力したデータのことを一般に標準入力として表現しても良さそうです。

```bash
envader@172-19-1-2:~$ echo "どうもありがとう" #標準入力
```

## 標準出力
プログラムの実行結果を書き出す先のことを示し、一般的にはプログラムを実行した画面（端末）が出力先になっています。

また
- 標準出力 … 加工済みデータの出力先や、ログ、メッセージ等情報出力用
- プログラムの実行結果の出力先のデバイスです。

と@angel_p_57さんの記事や[Envader](https://envader.plus/course/1/scenario/1007)にて解説されていますので、出力された画面の結果のことを指していることと思って良いでしょう。

```bash
envader@172-19-1-2:~$ echo "どうもありがとう"#標準入力
どうもありがとう#標準出力
```

## 標準エラー出力
`プログラムの正常動作とは関係のないエラーメッセージなどの出力ストリームで、デフォルトは画面（端末）`とLinuCの教科書では表現されていて、私的にはこの表現が一番しっくりきたかなと。

つまり何らかのエラーメッセージなどですね。

これまで書いてきた3つの表現への理解が難しいですが、このエラー出力だけが私の中で理解しやすかった表現で内容でした。

日本語って難しいですね。

```bash
envader@172-19-1-2:~$ eho "どうもありがとう"
-bash: eho: command not found#標準エラー出力
```

## 実際にEnvaderのシナリオをやってみる

ここまでの解説(というかほぼほぼ引用してしまっているが)を踏まえた上で、Envaderを体験してみたので一部だけ内容をご紹介。

### 標準出力だけを捨てて、標準エラー出力を取得せよ

[Envader](https://envader.plus/)では問題を解きながらどのような動きをするのかを学べるのですが、今回のミッションは標準エラー出力のみを取得するのがミッション。

まずはプログラムを実行してみる。

```bash
envader@172-19-1-2:~$ ./main
527ae859f224d464449c4ce1fb148defca7626650f6d7c7ec31784e529deb645
```

おお！何やらたくさんの文字列が出てきた。
これはプログラムを実行しているだけなのですが、これだと標準出力と標準エラー出力両方を表示している状態になってるみたいで、これだと正解にはなりません。

問題では標準エラー出力を取得することがミッション。。。。

標準エラー出力だけを取得(表示)させるにはどうすればいいか。
考えてみる。

### /dev/nullへの出力

`/dev/null`は、何かしらの出力をここへリダイレクトして捨てる事ができる場所です。
ただ、ここにリダイレクトしたものは復元する事ができないという事なので、`ゴミ箱`という表現とは少し違うみたいです。


ここで出てきたリダイレクトですが、今回の場合だと出力したデータを指定した`/dev/null`へ繋ぎ変える行為です。

### /dev/nullって何なの？
`/dev(device directory)は`ルートディレクトリ配下に存在している様々なデバイスファイルをまとめたディレクトリと紹介されています。

デバイスファイルとは、OSが検出、認識したデバイスを操作するためのスペシャルファイル(実際はインターフェイス)らしい。

また表現が難しいですが。。。。

とりあえず、`/dev`ディレクトリにはデバイスを扱うためのいろんなファイルが格納されている。という認識のもと、次に進みます。

続いて`null`について見て行きます。
`null`はデバイスファイルで、`nullデバイス`や`ビットバケツ`と呼ばれることもあるとか。

こちらは`仮想的なゴミ箱`とされていて、擬似デバイスと呼ばれているそうです。

以上を踏まえると、`dev/null`は仮想的なデバイスを扱うためのデバイスファイルに該当する。とのこと。
なんかもう、たくさんの言葉が出てきて頭がパンクしそうです。。。

ただ、先ほども書きましたが、ここへリダイレクトしたデータは全て破棄されてしまうため、復元不可能で再度データを読み込もうとしても何も読み込まれなくなってしまうので、注意が必要です。

## 標準出力を/dev/nullへ捨ててみる

それではやっとこ、標準出力のみを指定して`/dev/null`へ破棄させて行きましょう。
標準出力を指定するには、最初にご紹介した***ファイルディスクリプタ***を使用します。

`プログラムが扱う操作の対象とするファイルをOSが識別するための番号`がファイルディスクリプタでしたね。

| 番号(ファイルディスクリプタ) | 入出力名 | デフォルト |
|:-:|:-:|:-:|
| 0  | 標準入力  | キーボード  |
| 1  |  標準出力 | 画面(端末)  |
| 2 | 標準エラー出力 | 画面(端末) |

ファイルディスクリプタの標準出力は`1`に該当するので、こいつを指定して`/dev/null`へ出力を破棄してやればいけるはず。

```bash
#./mainの後ろはスペースで、その他はスペースは入れない！ファイルディスクリプタの1を指定。
envader@172-19-1-2:~$ ./main 1>/dev/null
57e5f2d6494ef18ec7260677c18e2db4
```

![スクリーンショット 2022-03-22 4.31.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1858576/e45b1dde-a128-03d6-78b4-e33d73f5bd56.png)

無事正解できました。

ここで間違えそうだったのが、標準エラー出力を取得したいから、`2`の標準エラー出力を指定してしまいそうになりました。

それだと`/dev/null`には標準エラー出力が破棄されてしまい、画面に出力されるのは標準出力が表示されてしまうので、ここが少しややこしいなと感じました。

こう書いていてもややこしい。



## まとめ
今回は標準入出力について、[Envader](https://envader.plus/course/1/scenario/1007)を利用して学んできました。

ファイルディスクリプタの考え方、標準入出力の表現や考え方など、言葉にして記事にするとなかなか難しいなと感じました。

実際のシナリオにはもっと詳細な解説があり、正直理解するのにはまだ時間がかかりそうです。
今後も復習として何度も解説を読みながら、参考記事も読み込んでさらに理解を深めていきたいと思います。

最後まで読んでいただきありがとうございました。

## 参考にさせていただいた記事と本

[標準入力・標準出力ってなに?](https://qiita.com/angel_p_57/items/03582181e9f7a69f8168)

LinuC教科書　レベル1

Linux標準教科書