bashのTips色々

# 概要

bashの記法は独特なものが多く毎回ググってしまうのでまとめて(と言いつつまとまりがないですが。。。)おこうと思います。
ある程度まとまってからpostしようとか思ってたらごちゃごちゃになっちゃいました。
bashで使えるという意味なのでposixシェル共通のネタも混ざってます。
随時更新します。参考になれば幸いです。

# 参考

* `man bash`
* [リファレンスマニュアル](http://www.gnu.org/software/bash/manual/bashref.html)
* [Advanced Bash-Scripting Guide](http://www.tldp.org/LDP/abs/html/)

# カッコ色々

bashでは色々なカッコがありますが、よく違いが分からず使っていたりするのでまとめてみます。

## []と[[]]

[]はtestコマンドのaliasです。[[]]じゃないとできないこととしては、以下のようなものがあります。

* 空白を含む文字列をクォートしなくてOK

	```bash:クォート不要
	var='abc 123'

	# []の中だとクォートしないとエラーになる
	[ $var = 'abc 123' ] && echo 'ok'
	-> brace.sh: line 6: [: too many arguments
	[ "$var" = 'abc 123' ] && echo 'ok'
	-> ok

	# [[]]の中だとクォートしなくてもOK
	[[ $var = 'abc 123' ]] && echo 'ok'
	-> ok
	```

* =~で正規表現が使える

	```bash:正規表現が使える
	var='abc 123'
	# [[]]の中だと正規表現が使える
	[[ $var =~ [a-z]{3}[[:space:]][1-9]{3} ]] && echo 'ok'
	-> oke
	```
bashの正規表現については[こちら](http://mywiki.wooledge.org/RegularExpression)が参考になります。

* パターンマッチもできる

	```bash:パターンマッチもできる
	var='abc 123'
	# [[]]の中だとパターンマッチができる
	[[ $var = abc* ]] && echo 'ok'
	-> ok
	[ $var = abc* ] && echo 'ok'
	-> brace.sh: line 18: [: too many arguments
	```

* AND条件OR条件に&&や||が使える

	```bash:OR条件に||が使える
	# []の中だとOR条件は-oを使う
	[ "$var" = 'abc 234' -o "$var" = 'abc 123' ] && echo 'ok'
	-> ok

	# [[]]の中だとOR条件に||が使える
	[[ $var = 'abc 234' || $var = 'abc 123' ]] && echo 'ok'
	-> ok
	```

その他詳しくは[こちら](http://mywiki.wooledge.org/BashFAQ/031)
ちなみに基本的には[]はPOSIXシェル準拠で[[]]はPOSIX準拠ではないです。
が、[[]]もbash以外にzshやkshでも実装されているものが多いので移植性を考えて利用を控えるとかはしなくても大丈夫なんじゃないかと思っています。
あと、[]は配列のインデックスを指す場合にも使いますが、そちらは説明するまでもないと思うので省略します。

## ()と(())

* ()は配列を定義するときに使います。

	```bash:配列定義
	var=(a b c)
	echo ${var[2]}
	-> c
	```

* (())は演算に使います。

	```bash:for文のカウンタとして使う
	for ((i=0;i<3;i++))
	do
		echo $i
	done
	-> 0
	   1
	   2
	```
	```bash:インクリメント
	var=1
	((var++))
	echo $var
	-> 2
	```
個人的によく間違えちゃうんですが、変数を定義するときは(())から含める必要があります。
`((var2=$var + 3))`と書くのが正しい($varはvarでもOKです)んですが、よく`var2=(($var + 3))`とか書いてエラーになっちゃいます。
この辺りは$(())のところでも後述します。

## {}と{{}}

* 変数展開
{}は$を付けて変数参照やら置換に使いますね。詳しくは後述します。

* 様々な展開
あとは変数に限らず色々な展開ができます。
例えば以下のようにfor文のカウンタを展開することができます。

	```bash:for文
	for i in {a..c}
	do
		echo $i
	done
	-> a
	   b
	   c
	```
この展開は結構便利でcpやmv使うときに重宝します。
例えばファイルのバックアップを取るときは以下のようにできます。

	```bash:cpと{}を使ってバックアップ
	cp /path/to/file{,_old}
	```
とかやると/path/toにfileのコピーとしてfile_oldが出来上がります。
フルパス2個打つとか無駄なことをしなくて済みます。

* コマンドをまとめる
{}を使うと複数のコマンドをひとまとめにできます。

	```bash:コマンドをまとめる
	{
		echo hoge
		echo fuga
		echo piyo
	} > echo.log
	```
ログ出力とかで重宝します。
全部のコマンドの後ろにリダイレクトを書くよりもキレイにできます。

続いて{{}}ですが、これは見たことないです。。。これって何かありますでしょうか？

## $()と$(())

* $()はコマンド展開です。``と同じ意味です。
たとえば/path/toにある各ファイルに対して何らかの処理を行う場合は以下の通りです。

	```bash:コマンド展開
	for i in $(ls /path/to)
	do
		echo $i
	done
	```

* $(())は演算で使います。(())と違ってインラインで使えます。

	```bash:$(())の演算
	echo $((1+3))
	-> 4
	# echo ((1+3))はエラーになる

	var=1
	var2=$(($var + 2))
	echo $var2
	-> 3
	# var2=((var + 2))はエラーになる
	```
この辺りは混同しがちなので要注意ですね。

## ''と""

カッコではないですが''と""の違いは変数が展開されないかとされるかの違いです。

```bash:''と""
var='abc'
echo '$var'
-> $var
echo "$var"
-> abc
```

# 宣言色々

bashの変数はデフォルトではグローバルです。
まずは以下を例にします。

```bash:sample.sh
#!/bin/sh

var1='var1 initial'

function func1 {
	var1='var1 changed'
	var2='var2 initial'
	echo $var1 # 3. 関数外で定義したグローバルな変数var1を書き換えたため'var1 changed'が表示される
	echo $var2 # 4. 関数内で定義したグローバルな変数var2の初期値'var2 initial'が表示される
}

echo $var1 #1. この時点では関数呼び出し前のため'var1 initial'が表示される
echo $var2 #2. この時点では関数呼び出し前のため''(空文字)が表示される
func1
echo $var1 #5. グローバルなvar1変数をfunc1関数内で書き換えたため、'var1 changed'が表示される
echo $var2 #6. func2内で定義したグローバルな変数var2の値が参照できるため、'var2 init'が表示される
```

挙動はコメントに書いたとおりですが、実行すると以下の結果が得られます。

```bash:sample.shの実行結果
var1 initial

var1 changed
var2 initial
var1 changed
var2 initial
```

## 変数宣言にlocalをつけて関数内localな変数にする

変数はlocalで宣言することでローカル変数とすることができます。
上記のスクリプトを以下のように書き換えます。

```:local.sh
#!/bin/sh

var1='var1 initial'

function func1 {
	local var1='var1 changed'
	local var2='var2 initial'
	echo $var1 # 3. 関数で定義したローカル変数var1の値'var1 changed'が表示される
	echo $var2 # 4. 関数内で定義したローカルな変数var2の初期値'var2 initial'が表示される
}

echo $var1 #1. グローバルな変数var1の値'var1 initial'が表示される
echo $var2 #2. var2は未定義のため''(空文字)が表示される
func1
echo $var1 #5. func1内で書き換えたのはローカル変数のため、グローバルなvar1変数の値は変わらず'var1 initial'が表示される
echo $var2 #6. func2内で定義したvar2はローカル変数のため、値が参照できず''(空文字)が表示される
```

これまた挙動はコメントに書いたとおりですが、実行すると以下の結果が得られます。

```bash:local.shの実行結果
var1 initial

var1 changed
var2 initial
var1 initial
```

## 変数宣言時に型指定する

変数宣言時に`declare -i var1`等とすると型を指定することができます。(ローカル変数の場合`local -i var1`等とできます。)
`-i`はintegerの意味で整数値を割り当てるようになります。

```bash:declareで整数を割り当てる
declare -i var1=123
echo $var1	# 123
var1=hoge
echo $var1	# 0
```
文字列を代入した場合にエラーにしてくれないのはちょっと残念ですね。

ちなみに代入する値が数式の場合は計算されます。

```bash:演算
var1=4/2
echo $var1	# 4/2
declare -i var1=4/2
echo $var1	# 2
```

その他使えるオプションは`help declare`で見ることができます。
(ちなみにbashのビルトインコマンドはmanでは出てこないのでhelpで見ます。cdとかもそうですね。)

## 変数宣言時にreadonlyを付けて読み取り専用変数(定数)にする

変数はreadonlyとして宣言することで読み取り専用にすることができます。

例えば、以下のスクリプトがあったとき、

```bash:readonly.sh
#!/bin/sh

readonly var1='hoge'
echo $var1

var1='fuga'
echo $var1
```

上記を実行すると、以下の結果が得られます。

```bash:readnoly.shの実行結果
hoge
readonly.sh: line 6: var1: readonly variable
hoge
```

var1の値は書き換えられずエラーになり、2回目の`echo $var1`では元の値が表示されます。
ちなみにdeclare -rとして宣言してもreadonlyと同じ効果が得られます。

## exportを使って別のシェルプロセスに変数を渡す

通常、あるシェルで定義した変数は別のシェルでは読めません。
しかし、exportを付けて宣言することで他のシェルに渡すことが可能になります。

たとえば以下のスクリプトがあったとき、

```bash:export.sh
#!/bin/bash
echo $var1
```

シェルから上記のスクリプトを実行すると以下のような結果になります。

```bash:export.shの実行結果
var1='hoge'
bash export.sh

#var1の値は別のシェルには渡らないため何も表示されない

export var1='hoge'
bash export.sh
-> hoge
#exportした変数var1の値は別のシェルに渡るため、親のシェルで設定した値が表示される
```

## sourceを使って別のシェルから変数をもらう

上と逆のパターンです。

例えば以下のスクリプトがあった場合、

```bash:source.sh
#!/bin/bash
var1='hoge'
```

実行結果は以下のようになります。

```bash:source.shの実行結果
$ bash source.sh
$ echo $var1

# source.shで定義した変数は受け取れないため何も表示されない

$ source source.sh
$ echo $var1
->hoge
#sourceコマンドで読み込んだスクリプト内で定義した変数var1の値が表示される。
```

シェル上での実行というよりは複数ファイル間で変数の受け渡しをする際によく使いますね。
ちなみに`source`は`.`で代替できます。

# 変数色々

bashの変数には様々なものがあります。

## $系

基本的には引数やらコマンド実行結果やらを示します。
以下のdollar.shを`./dollar.sh abc 123`と実行した場合をコメントで示します。

```bash:dollar.sh
#!/bin/bash
echo $0
# 実行コマンド。今回の場合はdollar.sh

echo $1
# 一つ目の引数。今回の場合はabc

echo "$*"
# 全ての引数。今回の場合はabc 123

echo "$@"
# 全ての引数。今回の場合はabc 123。上記との違いは後述

echo $#
# 引数の数。今回の場合は2

echo $$
# 実行中のプロセスID

echo $!
# 前回実行コマンドのプロセスID。今回の場合dollar.shを実行前のコマンドのPID

echo $?
# 前回実行コマンドの実行結果。成功の場合は0

echo $-
# スクリプト内で有効になっているフラグを表示します。

echo $_
# 実行シェルが取れます。./doller.shと実行していれば./doller.shが、bash doller.shと実行していれば/bin/bashが取れます。
```

フラグは[こちら](http://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)を参照してください。
`$*`と`$@`の違いは前者は引数全てを一つの文字列として扱うのに対して、後者はそれぞれ別に扱う点です。
基本的には`$@`を使うと良いと思います。

```bash:asterisk.sh
#!/bin/bash
for i in "$*"
do
	echo $i
done
```
```bash:実行結果
./asterisk.sh 123 abc
-> 123 abc
```
```bash:at.sh
#!/bin/bash
for i in "$@"
do
	echo $i
done
```
```bash:実行結果
./at.sh 123 abc
-> 123
   abc
```

* さらにコマンドラインでは以下のような$変数が使えます。
 * `!$`で前回実行コマンドの最後の引数を取得

		```bash:前回実行コマンドの最後の引数を取得
		less /path/to/file
		# とりあえず参照したけどやっぱり編集したいとかなったときは以下のようにする
		vi !$
		```
 * !!で前回実行コマンドの全体を取得

		```bash:前回実行コマンド全体を取得
		service httpd restart
		# とか実行したけどsudoしなきゃダメだったときは以下のようにする
		sudo !!
		```
!!は$入ってないですねｗ


## それ以外

$系以外にも色々な組み込み変数があります。
有名なのだと$HOMEとかですね。
書ききれないので[この辺](http://tldp.org/LDP/abs/html/internalvariables.html)を参考にしてもらえればと思います。

# 展開色々

## 変数操作

bashでは${}の中で色々な展開や置換等の変数操作が行えます。

例として、`path=/hoge/hoge/fuga/FUGA/piyo`のとき、

* 文字数を取得

	```bash:
	echo ${#path}
	-> 25
	```
* 部分文字列を取得

	```bash:
	echo ${path:1:4}
	-> hoge
	# 2文字目(インデックスが0始まりのため1は2文字目)から4文字を取得
	```
* 一つ目のhogeをhageに置換

	```bash:
	echo ${path/hoge/hage}
	-> /hage/hoge/fuga/FUGA/piyo
	```
* 全てのhogeをhageに置換

	```bash:
	echo ${path//hoge/hage}
	-> /hage/hage/fuga/FUGA/piyo
	```
* 小文字を大文字に変換

	```bash:
	echo ${path^^}
	-> /HOGE/HOGE/FUGA/FUGA/PIYO
	```
* 大文字を小文字に変換

	```bash:
	echo ${path,,}
	-> /hoge/hoge/fuga/fuga/piyo
	```
* ファイル名取得

	```bash:
	echo ${path##*/}
	-> piyo
	```

* ディレクトリ取得

	```bash:
	echo ${path%/*}
	-> /hoge/hoge/fuga/FUGA
	```

# リダイレクトとパイプ色々

## 基本

`>`がファイルへのリダイレクト、`>>`が上書き、`|`がパイプというのはさすがに説明不要かと思います。

* 標準エラー出力をリダイレクト
`ls /home/bin 2> hoge.log`

* 標準出力と標準エラー出力の両方をリダイレクト
`ls /home/bin /home/user1 &> hoge.log`
もしくは
`ls /home/bin /home/user1 >& hoge.log`
もしくは
`ls /home/bin /home/user1 > hoge.log 2>&1`

* teeで標準(エラー)出力とファイル両方に出力
`ls /home/user1 | tee hoge.log`

## execを使った色々

* 出力をファイルに書く。
`exec &>file`とすると以降のコマンド実行結果は全てfileに書かれます。
ただしエコーバックがなくなるので後述のプロセス置換、もしくはそもそもscriptコマンドを使ってしまった方が良いです。

* 任意のファイルディスクリプタを開く。(出力)
`3>`等とすることで出力用に任意のファイルディスクリプタを開くことができます。

	```bash:コマンド実行結果をファイルに書く
	exec 3>file
	echo "hoge" >&3
	exec 3>&-
	```
上記のように実行するとhogeがfileに書かれます。
最後は`3>&-`として忘れずにファイルディスクリプタを閉じましょう。
なお、3は標準出力(1)と標準エラー出力(2)以外の任意の数字でOKです。

* 任意のファイルディスクリプタを開く。(入力)
`3<`等とすることで入力用に任意のファイルディスクリプタを開くことができます。

	```bash:fileからgrepする
	exec 3<file
	grep "hoge" <&3
	exec 3<&-
	```
fileからhogeという文字列をgrepしています。
最後は忘れずに閉じましょう。

* 任意のファイルディスクリプタを開く。(入出力)
`3<>`等とすることで入出力用に任意のファイルディスクリプタを開くことができます。

	```bash:入出力
	echo "hoge" > file
	echo "fuga" >> file
	echo "piyo" >> file
	exec 3<> file
	read var1 <&3
	echo $var1	#hoge
	echo 'puyo' >&3
	exec 3>&-
	exec 3<&-
	```
とやるとfileの中身は

	```
	hoge
	puyo
	piyo
	```
になります。readで読んだ位置からpuyoを書くためです。

* webページをgetする
上記を応用するとこんなことができます。

	```bash:yahooをget
	exec 3<>/dev/tcp/www.yahoo.co.jp/80
	echo -e "GET / HTTP/1.1\n\n" >&3
	cat <&3
	```

## 実は順番はどうでも良い

もはや小ネタレベルですが、実は下の3つはどれも同じ意味です。

```bash:同じ意味
echo 'hoge' >hoge.txt
>hoge.txt echo 'hoge'
echo >hoge.txt 'hoge'
```

# read色々

readコマンドを使うとファイルとか変数から値を読むことができます。

```bash:read.txt
field1 \field2 field3 fiedl4
f1 \f2 f3 f4
```
```bash:read.sh
#!/bin/bash
while read var1 var2
do
	echo $var1
	echo $var2
done < read.txt
```
```実行結果
field1
field2 field3 fiedl4
f1
f2 f3 f4
```

## いらないフィールドを捨てる

ファイルの中に不要なフィールドある場合は`_`で捨てることができます。
(`_`じゃなくても任意の文字列で大丈夫ですがgolangっぽく`_`にしてみましたｗ)

```bash:read.sh
#!/bin/bash
while read var1 var2 _
do
	echo $var1
	echo $var2
done < read.txt
```
```:実行結果
field1
field2
f1
f2
```

## エスケープ回避

上記の通りテキストに`\`が含まれる場合、エスケープされてしまいます。
テキストに書かれている通り出力する場合は-rを付けます。

```bash:read.sh
#!/bin/bash
while read -r var1 var2 _
do
	echo $var1
	echo $var2
done < read.txt
```
```:実行結果
field1
\field2
f1
\f2
```

## 変数を読む

ヒアストリング(後述)とかを使うと変数から値を読むことができます。

```bash:read.sh
#!/bin/bash
var='a b c d'
read -r var1 var2 _ <<<$var
echo $var1
echo $var2
```

## その他

* IFSを使うと区切り文字を変えることができます。
`IFS=- read -r var1 var2 <<< "a-b-c-d-e"`

* -aを使うと値を配列に読み込むことができます。
`IFS=- read -ra parray <<< "a-b-c-d-e"`

ということで例によって詳しくは`help read`でお願いしますｗ

# &&や||で条件が書ける

簡単な条件判定なら&&や||を使うのがオススメです。

```bash:&&や||を使った条件式
# 条件がtrueの場合に実行
[ 1 -lt 2 ] && echo "1 is less than 2"
-> 1 is less than 2

# 条件がfalseの場合に実行
[ 1 -gt 2 ] || echo "1 is NOT greater than 2"
-> 1 is NOT greater than 2

# if else的な
[ 1 -gt 2 ] && echo "1 is greater than 2" || echo "1 is NOT greater than 2"
-> 1 is NOT greater than 2
```

ただし複雑な条件を書く場合は可読性を考えてif文を使ったほうが良いと思います。

# ヒアドキュメントとヒアストリング

## ヒアドキュメント

`<<`はヒアドキュメントと呼ばれ、文字列リテラルをコマンドに渡したりすることができます。
sqlplusやmysqlコマンド等でインラインでSQL実行したい場合等によく使います。

* 基本
基本的な使い方は以下の通りです。`_EOF_`は任意の文字列でOKです。
mysqlのインタラクティブなシェルは開かず、sqlが実行されて結果がターミナルに返ります。

	```bash:ヒアドキュメント
	mysql -u user -ppassword -D database <<_EOF_
	select * from test_table
	where id=1
	_EOF_
	```

* リダイレクトとパイプ
リダイレクトとパイプは以下のようにします。

	```bash:リダイレクト
	mysql -u user -ppassword -D database <<_EOF_ > output.log
	select * from test_table
	where id=1
	_EOF_
	```
	```bash:パイプ
	mysql -u user -ppassword -D database <<_EOF_ | grep hoge
	select * from test_table
	_EOF_
	```

* エスケープ
ヒアドキュメントには変数を渡すこともできます。

	```bash:変数渡し
	id=1
	mysql -u user -ppassword -D database <<_EOF_
	select * from test_table where id=${id}
	_EOF_
	```
変数をエスケープして文字列リテラルとして渡したい場合は以下のよう`_EOF_`の前に`\`を付けます。

	```bash:エスケープ
	id=1
	mysql -u user -ppassword -D database <<\_EOF_
	select * from test_table where id=${id}
	_EOF_
	```

* インデント
インデントしたい場合、以下のように書くとエラーになってしまいます。

	```bash:インデントできない
	for i in {1..2}
	do
		cat << _EOF_
		loop $i
		_EOF_
	done
	```
インデントしたい場合は以下のように<<-とします。

	```bash:インデント
	for i in {1..2}
	do
		cat <<- _EOF_
		loop $i
		_EOF_
	done
	```




## ヒアストリング

`<<<`はヒアストリングと呼ばれ、ヒアドキュメント同様文字列をコマンドに渡したりすることができます。

```bash:ヒアストリング
mysql -u user -ppassword -D database <<<"select * from test_table"
```
ただしインデントを付けたりはできません。(実は方法があったら教えてください)
可読性等を考えると基本的には1行のとき以外はヒアドキュメントの方が良いと思っています。
ちなみにリダイレクトとパイプは問題なくできます。

```bash:ヒアストリングのリダイレクトとパイプ
mysql -u user -ppassword -D database <<<"select * from test_table" > output.log
mysql -u user -ppassword -D database <<<"select * from test_table" | grep hoge
```

# プロセス置換

プロセス置換とはプロセスの結果を一時ファイルのようにコマンドに渡したりできる機能です。

## <()を使って一時ファイル不要のdiff

一番よく使うのがこのケースだと思います。
こんな感じのことができます。

```bash:一時ファイル不要のdiff
diff <(sort file1.txt|uniq) <(sort file2.txt|uniq)
```

file1の中身とfile2の中身をsortしてuniqしたものを比べてます。
プロセス置換を使わないと一時ファイルに吐き出したものをdiffする必要があります。

## >()を使ってコマンド出力結果をログに出力

`>()`は個人的にはあまり使いません。
以下のようにすると以降のコマンド実行結果は全てログにも出力されるようになります。

```bash:ログ出力
# 標準出力
exec 1> >(tee -a out.log)
# 標準エラー出力
exec 2> >(tee -a err.log >&2)
```

`>()`はこれくらいでしか使わない上、この用途もscriptコマンド使ってしまうことが多いです。
標準出力と標準エラー出力を分けたい(もしくはどちらかのみ取りたい)ケースでたまに使います。
一番よく使うケースとしてはstraceの実行結果をログに出すときに標準エラー出力に出すパターンで使ったりします。

# コマンド履歴

## historyに日時を残す

`HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "`
historyコマンドの結果が変わります。
~/.bash_historyのエントリーには#付きでUNIXタイムが刻まれます。

## 現在のセッションの履歴を残さない

`unset HISTFILE && exit`

## 履歴全削除

`HISTFILESIZE=0 && exit`

# スクリプトにセットする推奨オプション

上でも少し出てきましたが、setコマンドを使うとbashの挙動を制御することができます。
bashのスクリプトを実行するときに個人的によく付けるオプションは`set -euo pipefail`です。
それぞれ以下の通りです。

## -e

スクリプト内のコマンドで1つでもエラー(0以外の戻り値を返すもの)があるとスクリプトをそこで停止します。

```bash:-eフラグ無し
#!/bin/bash
ls /path/to/nonexist
echo hoge
-> ls: cannot access /path/to/nonexist: No such file or directory
   hoge # lsでエラーになってもhogeが出力される
```
```bash:-eフラグあり
#!/bin/bash
set -e
ls /path/to/nonexist
echo fuga
-> ls: cannot access /path/to/nonexist: No such file or directory
   # lsがエラーでスクリプトが終了するためecho fugaは実行されない
```

ここで問題になるのが、正常終了でも戻り値が0でないコマンドです。
また、実行結果がエラーでも一時的に無視したいケースもあるかと思います。
そんなときはコマンドの後ろに|| trueを付けるか、一時的に-eを無効にします。

```bash:一時的にエラーが出ても進みたい場合
# 回避パターン1
/command/that/returns/nonzero || true
# 上は必ず戻り値($?)が0になる

# 回避パターン2
set +e
/command/that/returns/nonzero
set -e
```

## -u

未定義の変数呼び出しを行うとエラーとします。変数名の打ち間違い等で意図した挙動にならないなんてことを防いでくれます。

```bash:-uフラグ
/bin/bash
var='123'
var2=${var1}'abc' #間違えてvarではなくvar1としてしまった

echo $var2
# var1は空なのでabcが出力されてしまう

set -u

var2=${var1}'abc' #ここで'var1: unbound variable'というエラーになる
echo $var2 # set -eもつけていればここが出力されずに停止する
```
ここで困るのが`$1`等のコマンドラインからの引数を受け取る際に、引数が任意の場合です。
必ず引数を渡すなら良いですが、任意の場合は`$1`などを使った際に未定義でエラーになってしまい使い勝手が悪いです。
そんなときはデフォルト値を使うと良いです。

```bash:default.sh
#!/bin/bash

set -u

var=${1:-default value}
echo $var
```
上記を`./default.sh`と実行した場合は`default value`が表示され、`./default.sh hoge`と実行した場合は`hoge`と表示されます。

## -o pipefail

パイプでコマンドを繋げた時にパイプのどれか1つでも0以外の戻り値を返したらその戻り値(複数あった場合は最も右側)を返すというものです。
デフォルトだと最後の戻り値が返るため、エラーハンドリングが正しくできなかったりします。

```bash:pipefailフラグ
sort /path/to/nonexist | uniq
echo $?
# uniq自体は成功するため0が返る

set -o pipefail

sort /path/to/nonexist | uniq
echo $?
# sortが失敗した戻り値の2が返る
```

スクリプトを書く際は上の3つを付けるようにしています。
他にもデバッグ時には-xや-vも付けます。

# EXITをフックする

スクリプト内で作った一時ファイルの削除等、エラーが起きた場合でも必ず実行したい処理というものはよくあると思います。
このような場合はEXITをフックして処理を実行することができます。
例えば以下のスクリプトは処理開始時に作成した一時ファイル格納ディレクトリをスクリプト終了時に必ず削除します。

```bash:EXITをフックする例
#!/bin/bash

# 一時ファイルを格納するディレクトリを作成
tmpfile=$(mktemp -d)

# スクリプト終了時に必ず実行したい処理を記述
function finally {
	rm -rf $tmpfile
}
# trapコマンドでEXITシグナル受信時にfinally関数が実行されるようにする
trap finally EXIT

echo 'start' > $tmpfile/file1
cat $tmpfile/file1
```

# 補完

## bash completion

bashの補完は非力なので[bash-completion](http://bash-completion.alioth.debian.org/)を導入すると様々なコマンドの補完ができるようになります。
sshコマンドやserviceコマンド等の補完ができるようになるため導入必須かと思います。
対応しているコマンドは/usr/share/bash-completion/completions/以下に設定ファイルが置かれます。
手元の環境(Ubuntu14.04)で見ると500コマンド以上が対応しているようです。
独自コマンドを追加したい場合は[こちら](http://www.debian-administration.org/article/317/An_introduction_to_bash_completion_part_2)にやり方が書かれています。
結構簡単に追加できます。

## rlwrap

sqlplusやmysql、redis等、独自のシェルが起動するコマンドを実行するとコマンド履歴や補完が使えなくなります。
rlwrapを導入すると履歴や補完が使えるようになります。
導入は`apt-get install rlwrap`や`yum install rlwrap`するだけです。(CentOSの場合はepelレポジトリが必要です)
rlwrapは文字通りreadlineをラップするだけなのでデフォルトだと補完といっても標準のシェルのコマンド補完しかできません。
なので例えばsqlplusでsqlを補完したい場合などは[こちら](http://www.linuxification.at/rlwrap_ext.html.en)の拡張を入れたりするとより便利になります。

# GLOBIGNORE

GLOBIGNOREを使うとワイルドカード利用時の除外ファイルを指定できます。
例えば、末尾にorgがついてるファイルを除外してlsしたいとか、testって文字が入ったファイルを除外してvimで開きたいとかって時に便利です。

```bash:GLOBIGNOREの使い方
$ ls
fuga.go  fuga_test.go  hoge.go  hoge_test.go

$ GLOBIGNORE="*test*"

$ vi *
2 files to edit
-> hoge.goとfuga.goだけ開ける
```


# それでも不満があれば

実行速度とか機能で不満が出てきたらzshとかに移行しましょうｗ

以上です。
随時更新します。