検索ではあんまり出ないbashの便利技

bashでは記号類をたくさん使うので、この書き方は何？と思っても検索でなかなか出てこないことがあると思う。
そこで知っていると便利なコマンドを残しておく。
随時追記予定。

確認に使用したbashのバージョンは以下のとおり。

```shell-session
[root@localhost tmp]# rpm -qa | grep bash
bash-4.1.2-33.el6.x86_64
```

## シェルの文法チェックをしたい
シェルの起動時に `-n` オプションを与えてやれば、シェルを実行せずに文法チェック
を行うことができる。`sh` でも使用可能。

```shell-session
[root@localhost tmp]# bash -n test.sh #文法エラーなし
  // 出力なし
[root@localhost tmp]# bash -n miss.sh #文法エラーあり
miss.sh: line 4: syntax error near unexpected token `done'
miss.sh: line 4: `done'
```

## シェルの実行内容をトレースしたい
`-x` オプションをつける。デバッグに便利。
`sh` でも使用可能。

```shell-session
[root@localhost tmp]# bash -x test.sh
  //省略
```

## シェルをステップ実行したい
擬似シグナル`DEBUG`を使う。擬似シグナル`DEBUG`はシェルが文を実行するたびに発行されるので、以下のようにうまくtrapしてやる。

```shell-session
[root@localhost tmp]# cat debug.sh
trap 'read -p "next(LINE:$LINENO)>> $BASH_COMMAND"' DEBUG

a=10
if [ $(( $a % 2)) -eq 0 ]; then
        echo "even"
fi
[root@localhost tmp]# bash -x debug.sh
+ trap 'read -p "next(LINE:$LINENO)>> $BASH_COMMAND"' DEBUG
++ read -p 'next(LINE:3)>> a=10' 
next(LINE:3)>> a=10 #ここで入力待ち
+ a=10
++ read -p 'next(LINE:4)>> [ $(( $a % 2)) -eq 0 ]'
next(LINE:4)>> [ $(( $a % 2)) -eq 0 ] #ここで入力待ち
 (省略)
```


## 未定義の変数をエラーとして扱いたい
`set -u` をつける。`rm -rf` とか意図せずシェル変数が未定義だった場合事故につながりそうなシェルスクリプト等に入れておくとよい。
`sh` でも使用可能。


```
[root@localhost tmp]# echo ${HOGE}
 //実行される
[root@localhost tmp]# set -u
[root@localhost tmp]# echo ${HOGE}
-bash: HOGE: unbound variable
```

## 終了ステータスが0以外ならその時点で打ち止めにしたい
`set -e` 単純なスクリプトかつ何かあったら後続を実行したくないときに使用する。
終了ステータスが0以外を返すタイミングはいろいろあるので、それほど出番はない。

```shell-session
[root@localhost tmp]# cat nosete.sh
echo ${HOGE}
echo "FUGA"
[root@localhost tmp]# bash -x nosete.sh
+ echo

+ echo FUGA
FUGA
[root@localhost tmp]# cat sete.sh
set -ue
echo ${HOGE}
echo "FUGA"
[root@localhost tmp]# bash -x sete.sh
+ set -ue
sete.sh: line 2: HOGE: unbound variable
 //ここで打ち止め
```

## コマンド中の重複した入力内容を省略したい

ブレース展開と呼ばれる機能が便利。
たとえば、hoge000, hoge111, hoge222等のようにhoge部分が共通しているとき、以下のような記載が可能。
```
[root@localhost tmp]# echo hoge{000,111,222}
hoge000 hoge111 hoge222
```

ブレース`{}`の中は空文字でもよく、たとえば以下のような_bkをつけてコピーという使い方もできる。

```shell-session
[root@localhost tmp]# cp testfile{,_bk} #cp testfile testfile_bk と等価
[root@localhost tmp]# ls testfile*
testfile  testfile_bk
```

## コマンドの出力結果をファイルに吐かずに他のコマンドの引数として渡したい

プロセス置換という機能が便利。

```shell-session
[root@localhost tmp]# cat test.sh
　//省略
[root@localhost tmp]# wc <(cat hoge)
      0       3    1856 /dev/fd/63
```

標準入力から受け付けてくれるコマンドはリダイレクトでもよいが、
そうでないコマンド(` diff <(sort hoge) <(sort hoge2)` )などに使える。

## パイプで渡したコマンドの終了ステータスが知りたい

通常パイプでコマンドを渡すと、`$?` では最後に実行したコマンドの終了ステータスしか見ることができない。
パイプで渡したコマンドの終了ステータスが見たいときは、`PIPESTATUS`を見ればよい

```shell-session
[root@localhost tmp]# cat test.sh | cat | cat | cat | cat
　//省略
[root@localhost tmp]# echo ${PIPESTATUS[@]}
0 0 0 0 0
```

## シェル変数やカレントディレクトリを一時的に変更したい

サブシェルを使用する。`()`で囲んだコマンドはサブシェルとして扱われ、
シェル変数やカレントディレクトリの変更はその中で閉じる。

```shell-session
[root@localhost tmp]# pwd
/tmp
[root@localhost tmp]# (cd /usr; ls )
bin  etc  games  include  java  lib  lib64  libexec  local  sbin  share  src  [root@localhost tmp]# pwd
/tmp #サブシェル内のcdの影響を受けない
[root@localhost tmp]# var="test"
[root@localhost tmp]# (var="fuga"; echo $var)
fuga
[root@localhost tmp]# echo $var
test #シェル変数は変更されない
```


#ヒストリ関連のコマンド便利技
過去に打ったコマンドを効率よく再実行できる。

## 最後に実行したコマンドを再実行
`!!` でOK. 最近は十字キー↑で表示されるので、あんまりで番はない。

```shell-session
[root@localhost tmp]# pwd
/tmp
[root@localhost tmp]# !!
pwd
/tmp
```

## 前回のコマンドの最後の引数を使用する
`!$` を使う。以下のよう`mkdir`してから`cd`みたいなときに便利。

```
[root@localhost tmp]# mkdir hogedirectory
[root@localhost tmp]# cd !$
cd hogedirectory
```

ちなみに、最後の引数だけででなく、n番目の引数を`!:2`と書いてやれば取得できるが、
あんまり出番はない。

```shell-session
[root@localhost hogedirectory]# echo 1ban 2ban 3ban
1ban 2ban 3ban
[root@localhost hogedirectory]# echo !:2
echo 2ban
2ban
```

## xxxで始まる最後で打ったコマンドの再実行
`!xxx` でOK. 個人的には`service xxx restart` とかを`!ser` とか打ったりして使っている。 


```shell-session
[root@localhost tmp]# service sendmail restart
　//省略
[root@localhost tmp]# ... # 何かほかの作業
[root@localhost tmp]# !ser
service sendmail restart
　//省略

```

ちなみに、後ろに`:p` をつけると、そのコマンドを実行せずに確認だけできる。

```shell-session
[root@localhost tmp]# !ser:p
service sendmail restart
```

## 参考文献
上記の内容は以下の書籍に詳しく書いているので、
もっとシェルの動作を体系的に理解したいのであればオススメ。

[![参考文献](http://ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=4774146439&Format=_SL160_&ID=AsinImage&MarketPlace=JP&ServiceVersion=20070822&WS=1&tag=rso-22 "シェルスクリプト基本リファレンス")](http://www.amazon.co.jp/gp/product/4774146439/ref=as_li_ss_il?ie=UTF8&camp=247&creative=7399&creativeASIN=4774146439&linkCode=as2&tag=rso-22)

[シェルスクリプト基本リファレンス](http://www.amazon.co.jp/gp/product/4774146439/ref=as_li_ss_il?ie=UTF8&camp=247&creative=7399&creativeASIN=4774146439&linkCode=as2&tag=rso-22")

すごく長いが、[Man page of BASH](http://linuxjm.osdn.jp/html/GNU_bash/man1/bash.1.html)にもちゃんと網羅されている。