Bashのリダイレクション色々

2018-12-11
Bash
Linux

仕事でシェルスクリプト書くことが、たまにあるのですが参考になるコードをウェブで物色していたところ、以下のようなコードを見かけました。

1
jq <<< '{}'

不等号3つのパターンを初めてみたので、何だこれ?という感じになりまして、この機会に調べてみることにしました。

マニュアル

Bashのマニュアルにバシッと記載がありました。

Bash Reference Manual - 3.6 Redirections

不等号3つのパターンは、3.6.7 Here Stringsというんですね。知らなかった。

サンプル

せっかくなので、リダイレクションの全パターンについて、サンプルコードと結果を載せておきます。

サンプルで使うhoge.txtの中身は文字列hogeです。

3.6.1 Redirecting Input

1
2
$ sed -e 's/hoge/fuga/g' < hoge.txt
fuga

説明不要でしょうけど、mysqlとかでクエリ実行するときによく使いますね。

3.6.2 Redirecting Output

1
$ echo 'hoge' > hoge.txt

リダイレクションの動作を制御するためのnoclobber optionというものがあって、set -Cで有効化できるようです。そうすると、ファイルが存在した場合に上書きが出来なくなります。知らんかったぞ!

1
2
$ set -C; echo 'hoge' > hoge.txt
-bash: hoge.txt: cannot overwrite existing file

noclobber option設定時でも、上書きしたい場合は>|でリダイレクトすると強制上書き可能。

1
$ echo 'hoge' >| hoge.txt

これはいつか使えそうな気がする…。

3.6.3 Appending Redirecting Output

1
$ echo 'hoge' >> hoge.txt

これは追記するときの常套手段ですね。

3.6.4 Redirecting Standard Output and Standard Error

1
2
3
$ curl -sS hoge &> hoge.txt
$ cat hoge.txt
curl: (6) Could not resolve host: hoge

マニュアルには>&も同じだが、&>が好ましいと書いてある。互換性の問題のようですが。
なお、意味的には&>2>&1は同じとのこと。これからは&>を使おうかな、短くて覚えやすい。

また>&は数字や、ハイフンを出力してはいけないとのこと、それをすると意図しないファイルディスクリプタに出力してしまうと。

ここの理解はずっと若干曖昧なままになっているので、今度独立して調べよう。

3.6.5 Appending Standard Output and Standard Error

3.6.4の追記版ですね。

1
2
3
4
$ curl -sS hoge &>> hoge.txt
$ cat hoge.txt
curl: (6) Could not resolve host: hoge
curl: (6) Could not resolve host: hoge

別の書き方としては

1
2
3
4
5
$ curl -sS hoge >> hoge.txt 2>&1
$ cat hoge.txt
curl: (6) Could not resolve host: hoge
curl: (6) Could not resolve host: hoge
curl: (6) Could not resolve host: hoge

3.6.6 Here Documents

シェルスクリプトでHere Documents使ったことなかった…

1
2
3
4
5
6
7
8
#!/bin/bash

STR=`cat << DELI
hoge
fuga
DELI`

echo $STR

DELIはデリミター文字列なので、何でも良し。ポイントとしては文字列になるのではなく標準入力であるということです。上の例ではcatコマンドの実行結果として変数STRに代入しています。

3.6.7 Here Strings

ようやく本題ですが、不等号3つを重ねたものはHere Stringsです。Here Documentsの一行版と思えば良さそうです。こちらも標準入力として不等号左側のコマンドに対して与えられます。

1
2
$cat <<< hoge
hoge

公式マニュアルを見ると、[n]<<< wordという記述方法になっています。このnはファイルディスクリプタを表していて、デフォルトは標準入力ですが、任意のファイルディスクリプタに変更可能です。※ちなみに他のリダイレクションも同様にファイルディスクリプタを指定できるものはマニュアルに[n]の指定があります。

3.6.8 Duplicating File Descriptors

マニュアルの記載だと [n]<&word または [n]>&wordと書いてある。cronの設定とかで標準エラー出力2を標準出力1にするのは、この構文を使っているんですね。Duplicatingだから複製ということなのか、じゃあエラー出力自体は残るようなイメージになるけど、実際には2が1に合成されているので、名称と動作が直感的ではないのだろうか…

3.6.9 Moving File Descriptors

知らなかったし、使ったこともなかった。

[n]<&digit- または [n]>&digit-となっている。

むしろ意味的には、2<&1-の方がしっくりくる気がする。実際に試してみると、想像どおりの動きをしてくれる。

1
2
3
$ curl -sS hoge > /tmp/test 2>&1-
$ cat /tmp/test
curl: (6) Could not resolve host: hoge

3.6.8との違いは何なのだろうか…。これも今後の研究の余地がある。

3.6.10 Opening File Descriptors for Reading and Writing

[n]<>wordまったく意味不明、何に使うのだろう。

ネットで見つけたサンプルコードを参考にして下記のようなスクリプトを書く。

1
2
3
4
5
6
7
8
9
10
#!/bin/bash

exec 3<>/tmp/test

echo 'hoge1' >& 3
echo 'hoge2' >& 3
echo 'hoge3' >& 3
echo 'hoge4' >& 3

cat /tmp/test

実行するとこうなる。

1
2
3
4
5
$ sh hoge.sh
hoge1
hoge2
hoge3
hoge4

シェルスクリプトで標準入出力以外を使ったことがなかったので知らなかっただけで、そりゃ使えるよねぇという話でした。

まとめ

Bashのマニュアル面白い!発見が多い、というかもっと早く読んどけよという話ですが。