シェルの入出力制御あれこれ

シェルスクリプトはあまり触らないから忘れちゃうのでメモっとく。
主にパイプとかリダイレクトとかプロセス置換とか。

# パイプ

#### コマンドの出力を別のコマンドの入力にする

```bash
command1 | command2 | command3
```

#### 先頭n行を表示する

```bash
command | head -10
```

#### 末尾n行を表示する

```bash
command | tail -10
```

#### コマンドの出力を標準出力とファイルに出力する

```bash
command | tee file
```

# リダイレクト

#### ファイルの内容をコマンドの標準入力へ渡す

```bash
command < file
```

#### コマンドの出力をファイルへ出力する

```bash
command > file
```

#### コマンドの出力をファイルに追加する

```bash
command >> file
```

#### 標準エラー出力をファイルに出力する

```bash
command 2> file
```
`2`と`>`の間にスペースを入れてはいけない。

#### 標準エラー出力を標準出力に出力する

```bash
command 2>&1
```

#### 標準出力と標準エラー出力を別々のファイルに出力する

```bash
command 1> file1 2> file2
```

#### 標準出力と標準エラー出力を同じファイルに出力する

```bash
command > file 2>&1
command &> file
```
`1> file 2> file`や`2>&1 1> file`ではダメ

#### 出力を闇に葬り去る

```bash
command &> /dev/null
```

# ヒアドキュメント

#### コマンドに複数行の入力を渡す

```bash
command <<EOS
line1
line2
EOS
```

#### タブでインデントした複数行の入力を渡す

`<<-`を使うと行頭のタブを取り除いた文字列が渡される。

```bash
command <<-EOS
	line1
	line2
EOS
```
```bash:出力
line1
line2
```

#### 複数行の入力をファイルに出力する

`cat` コマンドに複数行の入力を渡し、リダイレクトでファイルへ出力する。

```bash
cat <<EOS > sample.txt
line1
line2
EOS
```
```shell-session:出力
$ cat sample.txt
line1
line2
```

# シェル変数

#### コマンドの結果を変数へ格納する

コマンドをバッククォートで囲む。
bashならばコマンドを```$( )```で囲む記法も使える。入れ子もOK。

```bash
VAR1=`command`
VAR2=$(command $(command2))
```

#### ファイルの内容を変数へ格納する

ファイル名を```$(cat )```で囲む。
bashならばファイル名を```$(< )```で囲む記法も使える。

```bash
VAR1=$(cat file)
VAR2=$(<file)
```

※詳細は ```man bash``` で Command Substitution (コマンド置換) の解説を読むこと。

#### 変数の値をファイルとしてコマンドへ渡す

ヒアストリング`<<<`を使用する。

```bash
command <<< $VAR1
```

# 複数コマンドの出力をまとめて制御する

{}で囲むと複数のコマンドの出力をまとめて制御できる。

```bash
#!/bin/bash
{
  command1
  command2  
} 1> file1 2> file2
```

execでも同じことができる。

```bash
#!/bin/bash
exec 1> file1
exec 2> file2
command1
command2
```

[シェルスクリプトが '> $logfile 2>&1' だらけにならなくて済んだ話 - 続・ラフなラボ](http://memo.laughk.org/2014/04/24/shell_exec_log.html)

# プロセス置換

`<(list)`や`>(list)`をプロセス置換という。これを使うとコマンドの結果をファイルとして扱ったりできる。

[bashのプロセス置換機能を活用して、シェル作業やスクリプト書きを効率化する - 双六工場日誌](http://sechiro.hatenablog.com/entry/2013/08/15/bash%E3%81%AE%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E7%BD%AE%E6%8F%9B%E6%A9%9F%E8%83%BD%E3%82%92%E6%B4%BB%E7%94%A8%E3%81%97%E3%81%A6%E3%80%81%E3%82%B7%E3%82%A7%E3%83%AB%E4%BD%9C%E6%A5%AD%E3%82%84%E3%82%B9)
[Bash のプロセス置換が便利な件 - 理系学生日記](http://kiririmode.hatenablog.jp/entry/20120908/p1)

#### command1とcommand2の結果をcommand3にファイルとして渡す

```bash
command3 <(command1) <(command2)
```
上は以下のように書いたのと同じ動作。

```bash
command1 > file1
command2 > file2
command3 file1 file2
```

#### command1の結果をcommand2とcommand3に渡す

```bash
command1 | tee >(command2) | command3
```

#### command1の標準出力をcommand2に、標準エラー出力をcommand3に渡す

```bash
command1 1> >(command2) 2> >(command3)
```

# ログ出力の実践例

#### 標準出力、標準エラー出力をファイルにも出力する

```bash
command 1> >(tee file1) 2> >(tee file2 >&2) 
```

#### file1に全ログ、file2にフィルタリングしたログを出力する

```bash
command 2>&1 | tee file1 | grep -i error > file2
```

#### file1に全ログ、file2と標準出力にフィルタリングしたログを出力する

```bash
command 2>&1 | tee file1 | grep -i error | tee file2
```

#### file1と標準出力に全ログ、file2にフィルタリングしたログを出力する

```bash
command 2>&1 | tee >(grep -i error > file2) | tee file1
```

#### 標準エラー出力の行頭に`[ERROR]`を付けて標準出力に出力する

```bash
command 2> >(awk '{print "[ERROR]", $0}')
```