Bash setを知る
なにげなくシェルの先頭に
set -eu
とおまじないのように付けてはいませんか?
setのことを知ると、よりシェルを便利に使えると思ったので、ちょっとsetを掘り下げていこうかなと思います。
setとは
シェルの属性の設定/解除を行うBashの組み込みコマンドです。
例えば、以下のようなsetをシェル冒頭に記述すると、コマンド実行時エラーが検出したら即座にシェルがexitします。
$ vim test.sh ------------------------ #!/bin/bash set -e ls -l hoge # 存在しないファイル echo "test" ------------------------ # 上記シェル実行 $ ./test.sh # set -eがない場合 #ls: cannot access hoge: No such file or directory #test # set -eがある場合(echo "test"が出力されない) #ls: cannot access hoge: No such file or directory
このsetにはどんなオプションがあるのだろう?
それを調べてみます。
オプション
それぞれのオプションの詳しい動作を見ていきます。
ちなみにオプションがない場合は、設定されているシェル変数が全て表示されます。
また、移行のオプションは「-*」とハイフンで記述していきますが、「+*」とプラスで記述すると、その属性の設定が解除されます。
-a
export test=hoge
としなくても、自動で変数などが環境にエクスポートするオプションです。
$ vim test.sh ------------------------ #!/bin/bash set -a a=hoge b=$(bash test2) echo ${b} ------------------------ $ vim test2 ------------------------ echo ${a} ------------------------ $ ./test.sh # set -aがない場合 #(なにも表示されない) # set -aがある場合 #hoge
-b
バックグラウンドのジョブのステータス報告を即座に行ってくれるオプションです。
# まずはオプションを設定しないパターン $ sleep 5s # 5秒後にEnterキー #[1]+ Done sleep 5s # 次にオプションを設定するパターン $ set -b $ sleep 5s # 5秒後に勝手に表示される # [1]+ Done sleep 5s
-e(-E/-T)
上述した通り、シェル内でエラーが発生した場合、即座に終了するオプションです。
$ vim test.sh ------------------------ #!/bin/bash set -e ls -l hoge # 存在しないファイル echo "test" ------------------------ # 上記シェル実行 $ ./test.sh # set -eがない場合 #ls: cannot access hoge: No such file or directory #test # set -eがある場合(echo "test"が出力されない) #ls: cannot access hoge: No such file or directory
さて、このオプションと組み合わせると便利なのが trap
コマンドです。
指定されたシグナルを検知したら、指定のコマンドを実行してくれるコマンドです。
今回の場合はERRシグナルを対象とします。
$ vim test.sh ------------------------ #!/bin/bash set -e trap 'echo error!!' ERR ls hoge # 存在しないファイル echo test ------------------------ $ ./test.sh #ls: cannot access hoge: No such file or directory #error!!
とても便利なのですが、シェル関数やサブシェルでのエラーはERRシグナルとして検知してくれません。
$ vim test.sh ------------------------ set -e trap 'echo error!!' ERR function test_function() { ls hoge # 存在しないファイル } test_function echo test ------------------------ $ ./test.sh # trapのコマンドが実行されていない #ls: cannot access hoge: No such file or directory
シェル関数やサブシェルでのエラーも検出したい場合は、
「-E」オプションを指定すると検出してくれるようになります。
$ vim test.sh ------------------------ set -eE trap 'echo error!!' ERR function test_function() { ls hoge # 存在しないファイル } test_function echo test ------------------------ $ ./test.sh #ls: cannot access hoge: No such file or directory #error!!
今回の「-E」はERR疑似シグナルに対してですが
「-T」はDEBUGとRETURNに対して同様の働きをします。
-f
パス名展開を無効にするオプションです。
この字面だと意味がわかりませんが、ワイルドカード指定などができなくなります。
$ ls test* #test test2 $ set -f $ ls test* #ls: cannot access test*: No such file or directory
-k
引数に指定された代入文が、そのコマンドの環境変数となります。
$ vim test.sh ------------------------ #!/bin/bash set -k bash test2 TEST=hoge ------------------------ $ vim test2 ------------------------ echo ${TEST} ------------------------ $ ./test1.sh # set -kがない場合 #(なにも表示されない) # set -kがある場合 #hoge
-m
ジョブ制御を有効にします。
システム上の対話的シェルではデフォルトで有効です。
これが有効だと、fg/bgコマンドがシェル内で使用できるようになります。
以下の例では、一時中断したものを再度実行しています。
$ sleep 60 # ここでCtrl+z #[1]+ Stopped sleep 60 $ jobs #[1]+ Stopped sleep 60 $ fg %1 #sleep 60
無効にすると当然ですが動きません
$ set +m $ sleep 60 # ここでCtrl+zしても無反応
恥ずかしながらあまりジョブ制御について知らなかったので、調べてみたところ大変便利なものと気付きました。
ジョブ制御については別の機会に記事に出来ればと思います。
-n
コマンドを読み込みはしますが、実行はせず、構文エラーのみチェックします。
$ vim test.sh ------------------------ #!/bin/bash set -e touch hoge function hoge() { ------------------------ $ ./test.sh # ./test.sh: line 7: syntax error: unexpected end of file
-t
コマンドを1つ読み込み、実行後終了します。 いまいち使い所がわからない。。。
$ vim test.sh ------------------------ #!/bin/bash # 「;」で区切らず、改行すると「set」コマンド実行後終了する… set -t ; echo test echo test2 ------------------------ $ ./test.sh # test
-u
設定されてない変数を展開しようとするとエラーが発生するようになります。
$ vim test.sh ------------------------ #!/bin/bash set -u echo ${test} ------------------------ $ ./test.sh # set -u を指定しない場合 # (何も表示されない) # set -u を指定している場合 # ./test.sh: line 4: test: unbound variable
-v/-x
入力されたコマンドを出力します。デバッグなどに便利です。
以下は「-v」の例です。
$ vim test.sh ------------------------ #!/bin/bash set -v for i in $(seq 1 3) do echo ${i} done ------------------------ $ ./test.sh #for i in $(seq 1 3) #do # echo ${i} #done #seq 1 3 #1 #2 #3
「-x」も同様にコマンドを出力するオプションなのですが、少し違います。
$ vim test.sh ------------------------ #!/bin/bash set -x for i in $(seq 1 3) do echo ${i} done ------------------------ $ ./test.sh #++ seq 1 3 #+ for i in '$(seq 1 3)' #+ echo 1 #1 #+ for i in '$(seq 1 3)' #+ echo 2 #2 #+ for i in '$(seq 1 3)' #+ echo 3 #3
はい、「-x」の場合はforが展開されています。
「-x」のほうがよりデバッグ用途に使いやすいかなと思います。
-B
ブレース展開を有効にします。デフォルトで有効です。
ブレース展開とは?というのは、下記の例を見てもらえればわかると思います。
$ vim test.sh ------------------------ #!/bin/bash set -B touch hoge{.1,.2} ------------------------ $ ./test.sh $ ls -l hoge* #hoge.1 hoge.2
という感じで展開されます。以下のようなこともできます。
$ vim test.sh ------------------------ #!/bin/bash set -B mkdir -p {test1,test2}/hoge/{fuga,piyo} ------------------------ $ ./test.sh $ tree #. #├── test1 #│ └── hoge #│ ├── fuga #│ └── piyo #├── test2 #│ └── hoge #│ ├── fuga #│ └── piyo #└── test.sh
-C
リダイレクト演算子( > , >& , <> )で既存ファイルへの書き込みができなくなります。
ただし、「>|」と書くと上書きできます。
$ vim test.sh ------------------------ #!/bin/bash set -C touch hoge # この書き方だと上書きできない echo "test" > hoge cat hoge # この書き方ならできる echo "test2" >| hoge cat hoge ------------------------ $ ./test.sh ./test.sh: line 4: hoge: cannot overwrite existing file test2
-P
シンボリックリンクに対してcdする際に、シンボリックリンクを辿らず、物理的ディレクトリ構造が使われるようになります。
$ mkdir test1 $ ln -s test1 test2 $ ls -l # test1 test2 $ cd test2 ; pwd # ~/test2 $ cd .. ; set -P ; cd test2 ; pwd # ~/test1
最後に
一通りオプションを見てきましたが、
有用そうなのもあれば、何に使うかわからないものもチラホラありました。
結局は自分がどのようなものを作りたいかにより利用するオプションは当然変わってくると思いますが、
とりあえず
set -axueE
あたりを付けておけば便利になるかなと思いました。