Linux IO リダイレクト

# 標準入出力

Linuxシステムでは、最も基本的なI/Oデバイスが3つあります。

1. 標準入力デバイス（stdin）：端末のキーボードに対応しています
2. 標準出力デバイス（stdout）：端末のディスプレイに対応しています
3. 標準エラー出力デバイス（stderr）：端末のディスプレイに対応しています

Linuxシステムでは、全てがファイルとして扱われるため、これら3つのデバイスもシステム内でファイルの形態で存在します。
プログラムは標準入力ファイルからデータを取得し、実行結果とエラー情報を標準出力デバイス及び標準エラー出力デバイスに出力します。
仮想端末では、標準入出力デバイスは現在の端末ウィンドウです。

```shell
[root@rocky86 ~]# ll /dev/std*
lrwxrwxrwx 1 root root 15 Jul 12 10:13 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 Jul 12 10:13 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 Jul 12 10:13 /dev/stdout -> /proc/self/fd/1
[root@rocky86 ~]# ll /proc/self/fd/
total 0
lrwx------ 1 root root 64 Jul 12 17:47 0 -> /dev/pts/1
lrwx------ 1 root root 64 Jul 12 17:47 1 -> /dev/pts/1
lrwx------ 1 root root 64 Jul 12 17:47 2 -> /dev/pts/1
[root@rocky86 ~]# tty
/dev/pts/1

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3515709/84879fbe-aed4-083e-ecc2-575b4394512b.png)

# I/Oリダイレクトについて

リダイレクト：redirect
I/Oリダイレクト：デフォルトの入出力やエラーの対象デバイスを変更し、新しいターゲットに指向させる機能です。

## 標準出力と標準エラー出力のリダイレクト
STDOUTとSTDERRはデフォルトで現在の端末を使用していますが、指定した端末やファイルにリダイレクトすることが可能です。
書式：
`COMMAND OPERATOR FILE|DEVICE`
### 上書きオペレータ、ファイルが存在する場合は上書きされます
```
> # STDOUTをファイルにリダイレクトします
1> # 上記と同じ
2> # STDERRをファイルにリダイレクトします
&> # 標準出力と標準エラーの両方をリダイレクトします
>& # 上記と機能は同じですが、上記の方法を推奨します
```
### 追加オペレータ、既存のファイルに内容を追加します
```
>> # 標準出力をファイルに追加リダイレクトします
1>> # 上記と同じ
2>> # 標準エラーをファイルに追加リダイレクトします
```

### 例：標準出力を他の端末へ
```shell
[root@rocky86 ~]# tty
/dev/pts/0
[root@rocky86 ~]# cat fstab > /dev/pts/1
[root@rocky86 ~]# ls > /dev/pts/1
```

### 例：標準エラー出力を他の端末へ
```shell
[root@rocky86 ~]# tty
/dev/pts/0
[root@rocky86 ~]# lss 2> /dev/pts/1
[root@rocky86 ~]# cat null 2> /dev/pts/1
```

### 例：ファイルへのリダイレクト
#### 標準出力をファイルにリダイレクト（上書き）
```shell
[root@rocky86 ~]# cat fstab > out.log
```
#### 標準出力をファイルにリダイレクト（追加）
```shell
[root@rocky86 ~]# cat fstab >> out.log
```
#### 標準エラー出力をファイルにリダイレクト（上書き）
```shell
[root@rocky86 ~]# cat null 2> err.log
```
#### 標準エラー出力をファイルにリダイレクト（追加）
```shell
[root@rocky86 ~]# cat null 2>> err.log
```

### 標準出力と標準エラー出力を別々にリダイレクト
書式：`COMMAND > success.out 2> /path/to/error.out`
#### 例：標準出力と標準エラー出力の同時リダイレクト
```shell
[root@rocky86 ~]# ls fstab null
ls: cannot access 'null': No such file or directory
fstab
[root@rocky86 ~]# ls fstab null > out.log 2> err.log
```

### 標準出力と標準エラー出力を一つのデータストリームに統合してリダイレクト
```
&> # 上書きリダイレクト
&>> # 追加リダイレクト
例：COMMAND > /path/to/file.out 2>&1
例：COMMAND >> /path/to/file.out 2>&1
```

#### 例：標準出力と標準エラー出力を同じファイルにリダイレクト
```shell
# 標準出力をout.logへリダイレクトし、標準エラー出力を標準出力にリダイレクト
[root@rocky86 ~]# ls fstab null > out.log 2>&1
# 標準エラー出力をout.logへリダイレクトし、標準出力を標準エラー出力にリダイレクト
[root@rocky86 ~]# ls fstab null 2> out.log 1>&2
[root@rocky86 ~]# ls fstab null &> out.log
[root@rocky86 ~]# ls fstab null >& out.log
```

#### 誤った記述例
```shell
# 標準エラー出力を標準出力にリダイレクトする前に、標準出力のファイルを先に定義する必要があります
[root@rocky86 ~]# ls fstab null 2>&1 > out.log
# 標準出力を標準エラー出力にリダイレクトする前に、標準エラー出力のファイルを先に定義する必要があります
[root@rocky86 ~]# ls fstab null 1>&2 2> out.log
```

#### 例：大きなファイルのクリア
```shell
# 通用的な方法
[root@rocky86 ~]# cat /dev/null > /data/file.log
# シェルに依存する、通用しない方法
[root@rocky86 ~]# > /data/file.log
```

#### 例：標準出力と標準エラー出力の入れ替え
```shell
# 子プロセスでファイルディスクリプタ3を借用して、標準出力と標準エラー出力を入れ替える
[root@rocky86 ~]# (ls fstab null 3>&1 1>&2 2>&3) > right.out 2> err.out
[root@rocky86 ~]# cat right.out
ls: cannot access 'null': No such file or directory
[root@rocky86 ~]# cat err.out
fstab
```


## 標準入力リダイレクトについて

`tr` コマンドは文字の変換、削除、重複した文字の圧縮を行うために使用されます。これは標準入力からデータを読み込み、結果を標準出力に出力します。

`tr [OPTION]... SET1 [SET2]`

### よく使われるオプション
- `-c|-C|--complement` SET1に含まれない文字をSET2で置換します
- `-d|--delete` SET1に含まれる全ての文字を削除し、変換しません
- `-s|--squeeze-repeats` SET1に含まれる重複した文字を圧縮し、削除します
- `-t|--truncate-set1` SET1をSET2で置換し、SET2が不足している場合は処理しません

### よく使われるワイルドカード
- `[:alnum:]` アルファベットと数字
- `[:alpha:]` アルファベット
- `[:digit:]` 数字
- `[:lower:]` 小文字
- `[:upper:]` 大文字
- `[:space:]` 空白文字
- `[:print:]` 印刷可能な文字
- `[:punct:]` 句読点
- `[:graph:]` グラフィック文字
- `[:cntrl:]` 制御（非表示）文字
- `[:xdigit:]` 16進数文字

### 例：文字の置換
```shell
[root@rocky86 ~]# tr 123 abc
123123
abcabc
1233333
abccccc
```
### 例：小文字を大文字に変換
```shell
[root@rocky86 ~]# tr a-z A-Z
123abcd
123ABCD
[root@rocky86 ~]# tr [:lower:] [:upper:]
123abc
123ABC
```
### 例：123ではない文字をxに置換
```shell
[root@rocky86 ~]# tr -c 123 x
13579
13xxxx
```
### 例：2～5以外の内容をxに置換
```shell
[root@rocky86 ~]# tr -c '2-5' x
123456789
x2345xxxxx
```
### 例：2～5を削除
```shell
[root@rocky86 ~]# tr -d '2-5'
1234567
167
```
### 例：大文字アルファベットの削除
```shell
[root@rocky86 ~]# tr -d [:upper:]
123abcABC456
123abc456
```

### 例：ファイル `/etc/issue` の小文字を大文字に変換し出力
```shell
[root@rocky86 ~]# tr [:lower:] [:upper:] < /etc/issue
\S
KERNEL \R ON AN \M
```
### 例：ファイル `/etc/issue` の小文字を大文字に変換しファイルにリダイレクト
```shell
[root@rocky86 ~]# tr [:lower:] [:upper:] < /etc/issue > tr.txt
```

## 標準入力リダイレクトについて

標準入力リダイレクトはファイルを使用してキーボード入力を代替するもので、ファイルからデータを読み取り、端末の入力デバイスとして代わりに入力するデータを処理します。ここでは、コマンドが標準入力をサポートしているかどうかを区別することが重要であり、どのコマンドでもパラメータなしで直接Enterを押すと標準入力を待機するかどうかを確認することで判断できます。

形式：
`COMMAND 0< FILE`
`COMMAND < FILE`
### 例：標準入力リダイレクトを使用
```shell
[root@rocky86 ~]# cat a.txt
1+2+3+4+5+6+7+8+9+10

[root@rocky86 ~]# bc < a.txt
55

[root@rocky86 ~]# bc < a.txt > rs.txt
[root@rocky86 ~]# cat rs.txt
55

[root@rocky86 ~]# seq -s + 10 > a.txt; bc < a.txt > rs.txt
[root@rocky86 ~]# cat rs.txt
55

# `cat a.txt` に等しい
[root@rocky86 ~]# cat < a.txt
1+2+3+4+5+6+7+8+9+10

# 入力リダイレクト後に出力リダイレクト
[root@rocky86 ~]# cat < a.txt > b.txt
[root@rocky86 ~]# cat b.txt
1+2+3+4+5+6+7+8+9+10
```

### 標準入力の多行リダイレクトについて

`"<<終止語"` コマンドはSTDINに複数行のリダイレクトをキーボードから行え、終止語までの全てのテキストがSTDINに送信されます。これはhere documentsとしても知られており、終止語には `!`、`@`、`$`、`EOF`（End Of File）、`magedu` など、1つまたは複数のシンボルを使用できます。`EOF` が一般的に使用されます。

### 例：複数行リダイレクトの使用
```shell
[root@rocky86 ~]# cat <<EOF
> 123
> 456
> EOF
123
456

[root@rocky86 ~]# bc <<EOF
> 2+3
> 3^4
> EOF
5
81

# 複数行をファイルに上書き
[root@rocky86 ~]# cat > abc.txt <<EOF
> 123
> 456
> 789
> EOF

[root@rocky86 ~]# cat abc.txt
123
456
789
```

### 例：メール送信
```shell
[root@rocky86 ~]# mail -s "test title" 123456@qq.com <<EOF
> hi
>
> this is test mail
>
> bye
> from mage
> EOF
```

## 高度なリダイレクト方法について
コマンド `cmd <<< "string"` は、ここから文字列がcmdのstdinに渡されることを表しているhere stringです。

### 例：直接文字列の入力
```shell
[root@rocky86 ~]# cat <<< "abc"
abc

[root@rocky86 ~]# bc <<< "1+2+3"
6

[root@rocky86 ~]# tr 'a-z' 'A-Z' <<< "welcome to magedu"
WELCOME TO MAGEDU
```

### 例：コマンド実行結果をリダイレクト入力として使用
```shell
[root@rocky86 ~]# bc <<< `echo 1+2+3`
6

[root@rocky86 ~]# bc <<< `seq -s + 3`
6

[root@rocky86 ~]# tr 'a-z' 'A-Z' <<< `hostname`
ROCKY86
```

コマンド `cmd1 < <(cmd2)`
名前はProcess substitutionであり、以下の2部分で構成されています。
`<(cmd2)` はcmd2の出力を一時ファイルに書き込むことを表しており、注意：`<` 記号と`(` 記号の間には空白がありません。
`cmd1 <` は標準のstdinリダイレクトです。
これらをまとめると、cmd2の出力stdoutが一時ファイルを介してcmd1にstdinとして伝達されることになります。

### 例：プロセス置換の利用
```shell
[root@rocky86 ~]# bc < <(echo 1+2+3)
6

[root@rocky86 ~]# bc < <(seq -s + 3)
6

[root@rocky86 ~]# tr 'a-z' 'A-Z' < <(hostname)
ROCKY86
```

### 例：中間ファイルの確認
```shell
[root@rocky86 ~]# ll <(hostname)
lr-x------ 1 root root 64 Jul 12 23:23 /dev/fd/63 -> 'pipe:[118809]'
```