なんとなしにBashのリダイレクトとパイプについて調べてたので、後から確認しやすいようにまとめてみることにする。

1.ファイルへのリダイレクト

基本的な使い方。コマンドの実行結果をファイルにリダイレクトする事で、上書きや追記をさせることが出来る。
これについては、UNIX系OSに慣れていない人でもよく知っている事だろう。
「>」で上書き(ファイルの既存内容は削除される)、「>>」で追記書き込みが行われる。

bash
コマンド > ファイル # 上書き コマンド >> ファイル # 追記

で、このリダイレクトだが通常は標準出力しかリダイレクトされない。
この辺りを使い分ける場合は、以下のようにコマンドを実行する。
※追記書き込み時も同様のため割愛。

bash
コマンド 1> ファイル # 標準出力のみをリダイレクト(デフォルト) コマンド 2> ファイル # 標準エラー出力のみをリダイレクト コマンド &> ファイル # 標準出力・標準エラー出力を共にリダイレクト

これを利用することで、以下のように標準エラー出力だけを/dev/nullにリダイレクトしてコンソールにもファイルにも出力させないようにしたり、それぞれを別のファイルに出力させたりすることができる。
ちょっとしたお遊びとしては、他のコンソールに任意の文字列を送りつけるといった事も可能だ。たとえば、以下のようにコマンドを実行することで「/dev/pts/1」を使っているコンソールに文字列を送ることができる。

bash
echo "文字列" > /dev/pts/1

ちなみに、標準エラー出力と標準出力を入れ替える場合は、以下のようにコマンドを実行する事で行える。

bash
コマンド 3>&1 1>&2 2>&3

2.ファイルの読込み

リダイレクトを使うことで、ファイルを一部のコマンドに読み込ませる事も出来る。

bash
コマンド < ファイル # ファイルの内容を一部のコマンドに読み込ませる

以下、readコマンドを用いた例

shell
[root@test-node ~]# cat /work/command /etc [root@test-node ~]# read -r line < /work/command ;echo $line /etc

3.複数行の文字列をコマンドに渡す

これもいろんな所でみる使い方だ。
以下のようにコマンドを実行することで、複数行の文字列をコマンドにリダイレクトすることが出来る。

bash
コマンド << EOF ...... EOF

よく使われるのは、catと組み合わせてファイルに書き込みをする方法だろう。
以下、例。

shell
[root@test-node ~]# cat << EOF > /work/test_red.txt > 1234 > 5678 > 90 > EOF [root@test-node ~]# cat /work/test_red.txt 1234 5678 90

ここで記述されているEOFというのは処理終了の判定文字列となる。
ここではEOFと記述しているが、他の文字列でも構わない。

なお、Tabなどを入力したい場合は、以下のようにコマンドを実行する。

bash
コマンド <<- EOF ...... EOF

4.コマンドに文字列をリダイレクトする

以下のように指定することで、コマンドに文字列をリダイレクトする事も出来る。

bash
コマンド <<< "文字列"

例えば、catコマンドに文字列を送ることでechoと同じような処理を行わせる事ができる。
(日常で使うかわからないけど)

shell
[root@test-node ~]# cat <<< "This Server is $(hostname)." This Server is test-node.

5.execコマンドと組み合わせる

execコマンド(forkせずにコマンドを実行する)と組み合わせる事で、エラーのリダイレクト設定をそのセッション内では継続させるような事もできる。
(ただし、PS1などがコンソールに出力されない状態になる場合もあるので、シェルスクリプト内でしか使えないと思われる)

以下、簡単にまとめ。

セッション中は永続的に標準エラー出力を指定したファイルにリダイレクトする

bash
exec 2>ファイル # セッション中は永続的に標準エラー出力を指定したファイルにリダイレクトする
shell
[root@test-node ~]# cat /work/test_sh.sh exec 2>/tmp/error_test greew grep 'aaa' /etc/hogeghogege [root@test-node ~]# sh /work/test_sh.sh [root@test-node ~]# cat /tmp/error_test /work/test_sh.sh: 行 2: greew: コマンドが見つかりません grep: /etc/hogeghogege: そのようなファイルやディレクトリはありません

ファイルを読込む

bash
exec 3<ファイル
shell
[root@test-node ~]# exec 3</etc/fstab [root@test-node ~]# grep "" <&3 # # /etc/fstab # Created by anaconda on Sun Jan 3 08:46:04 2016 # # Accessible filesystems, by reference, are maintained under '/dev/disk' # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info # /dev/mapper/centos-root / xfs defaults 0 0 UUID=461b7464-714d-4a8b-9f63-19fe0482887f /boot xfs defaults 0 0 /dev/mapper/centos-home /home xfs defaults 0 0 /dev/mapper/centos-swap swap swap defaults 0 0 [root@test-node ~]# grep "" <&3 [root@test-node ~]#
http
## ファイルへ書き込む ```bash exec 3>ファイル
shell
[root@test-node ~]# exec 3>/tmp/3teest [root@test-node ~]# echo "aaaaaaaa" >&3 [root@test-node ~]# cat /tmp/3teest aaaaaaaa

ファイルへ書き込み・読込みを行う

bash
exec 3<>ファイル
shell
[root@test-node ~]# exec 3<>/tmp/3test [root@test-node ~]# echo "aaaaaaaa" >&3 [root@test-node ~]# [root@test-node ~]# exec 3<>/tmp/3test [root@test-node ~]# grep "" <&3 aaaaaaaa

指定ポートに対し通信する

bash
exec 3<> /dev/tcp/アドレス/ポート番号 # tcpの場合 exec 3<> /dev/udp/アドレス/ポート番号 # udpの場合
shell
[root@test-node ~]# exec 3<> /dev/tcp/127.0.0.1/22 [root@test-node ~]# echo $? 0 [root@test-node ~]# exec 3<> /dev/tcp/127.0.0.1/23 -bash: connect: 接続を拒否されました -bash: /dev/tcp/127.0.0.1/23: 接続を拒否されました [root@test-node ~]# echo $? 1

6.グルーピングしてまとめてリダイレクトする

前にもここで書いた事があるが、「()」もしくは「{}」でコマンドを囲むことでまとめてリダイレクトさせることが出来る。

bash
(コマンド1;コマンド2;...) > ファイル # サブシェルあり { コマンド1;コマンド2;...; } > ファイル # サブシェルなし
shell
[root@test-node ~]# (echo aaa;echo bbb) > /tmp/aaa123 [root@test-node ~]# cat /tmp/aaa123 aaa bbb [root@test-node ~]# [root@test-node ~]# { echo aaa;echo bbb; } > /tmp/aaa123_2 [root@test-node ~]# cat /tmp/aaa123_2 aaa bbb

7.コマンドに複数コマンドの実行結果を渡す

一つのコマンドの実行結果を渡すだけなら「|(パイプ)」でいいのだが、時には複数コマンドの実行結果を渡したい時だってある。
その場合は、シェル芸勉強会などではよく見かけるのだが、以下のようにリダイレクトさせればよい。
(一応、この機能には「Process Substitution」という名前が付いているらしい)

bash
コマンド <(コマンド)

複数ある場合は、そのままスペース区切りで続けて記述すれば良いだけだ。

bash
コマンド <(コマンド) <(コマンド)
shell
[root@test-node ~]# cat /work/test_sort1 2 dddddd 1 aaaaaa 5 bbbbbb 6 gfgfgr [root@test-node ~]# cat /work/test_sort2 6 gfgfgr 4 bbbbbb 1 aaaaaa 3 cfffff [root@test-node ~]# sort /work/test_sort1 1 aaaaaa 2 dddddd 5 bbbbbb 6 gfgfgr [root@test-node ~]# sort /work/test_sort2 1 aaaaaa 3 cfffff 4 bbbbbb 6 gfgfgr [root@test-node ~]# diff <(sort /work/test_sort1) <(sort /work/test_sort2) 2,3c2,3 < 2 dddddd < 5 bbbbbb --- > 3 cfffff > 4 bbbbbb

8.コマンドの実行結果を他のコマンドに連携する(パイプ)

流石にこれは説明しなくてもいいだろう。
コマンドの実行結果をパイプで繋げる事で連携していく、UNIX系OSのシェルなら大体持っている機能だ。

bash
コマンド | コマンド
shell
[root@test-node ~]# grep "" /work/hostname_* /work/hostname_aaa_000.txt:aaa_000 /work/hostname_aaa_001.txt:aaa_001 /work/hostname_aaa_002.txt:aaa_002 /work/hostname_aaa_003.txt:aaa_003 /work/hostname_aaa_004.txt:aaa_004 /work/hostname_aaa_005.txt:aaa_005 /work/hostname_bbb_000.txt:aaa_000 /work/hostname_bbb_001.txt:aaa_001 /work/hostname_bbb_002.txt:aaa_002 /work/hostname_bbb_003.txt:aaa_003 /work/hostname_bbb_004.txt:aaa_004 /work/hostname_bbb_005.txt:aaa_005 [root@test-node ~]# grep "" /work/hostname_* | grep aaa_000 /work/hostname_aaa_000.txt:aaa_000 /work/hostname_bbb_000.txt:aaa_000

なお、bash4.1以降の場合だと、以下のようにコマンドを実行することで標準エラー出力を渡す事も出来るようになったようだ。

bash
コマンド |& コマンド
shell
[root@test-node ~]# grep "" /work/hostneme_* grep: /work/hostneme_*: そのようなファイルやディレクトリはありません [root@test-node ~]# [root@test-node ~]# grep "" /work/hostneme_* | sed 's/work/aaaa/g' grep: /work/hostneme_*: そのようなファイルやディレクトリはありません [root@test-node ~]# [root@test-node ~]# grep "" /work/hostneme_* |& sed 's/work/aaaa/g' grep: /aaaa/hostneme_*: そのようなファイルやディレクトリはありません

9.パイプでつなげた各コマンドの実行結果(exitcode)を確認する

パイプでつなげた場合、途中のコマンドでエラーが出てもecho $?ではパイプの最後にあるコマンドの結果しか表示されない。
各コマンドのexitcodeを取得する場合は、bash変数のPIPESTATUSから取得すると良いだろう。

bash
echo ${PIPESTATUS[@]}
shell
[root@test-node ~]# grep "" /euc |& sed 's/euc/aaa/g' grep: /aaa: そのようなファイルやディレクトリはありません [root@test-node ~]# echo ${PIPESTATUS[@]} 2 0