Bashの括弧のノウハウ（まとめ）

すでにQiitaにもいくつか記事があるが [^1]、Bashでの各種の括弧はそれぞれ意味がある。
自分でも知らなかったBashの括弧もあったので、改めてまとめてみた。

なお、Bashの話ではあるが、Bourne shell（`/bin/sh`）としても動くサンプルコードは `sh` コードとしてある。

[^1]: 例えばこちらの記事　https://qiita.com/yohm/items/3527d517768402efbcb6

## bracket [ ]

shからある伝統的なコマンドで、`test` と等価。式の結果を、0が真、1が偽の真偽値で返す。
`if` と組み合わせて用いることが多い。
そう、シェルでは`[`はコマンドなのである。[^aboutcommand]
`[`と`]` の前後にはスペースが必要で、後続のコマンドがある場合は`;`で終わらせなければならない。なぜなら前述のとおり `[`はコマンドで、`[` から `]` までが一連のコマンドと引数の組み合わせだからだ。（`]`は必須な引数という位置付け）

[^aboutcommand]: /bin ディレクトリの中を確認してみよう

```bash:sh
if [ "x$1" = "x--help" -o "x$1" = "x-h" ]; then
  show_help    # help
fi
```

この場合の式で `"x$1"` のように `x` といれている（何の文字列でもいい）のは、例えば`$1`が空文字列だった場合にエラーになるから。先人の工夫である。

なお、論理式は`-a`や`-o`を使う方法の他に、`&&`や`||`を使って表現することも可能。
ただ前者は優先順位はあるのだが、後者の場合はコマンドとして順に処理されるので優先順位がない。

```bash:sh
if [ "x$OPT" = "x-h" -o "x$OPT" = "x--help" ]; then
  help
fi
if [ "x$OPT" = "x-h" ] || [ "x$OPT" = "x--help" ]; then
  help
fi
# if を無くした形
[ "x$OPT" = "x-h" ] || [ "x$OPT" = "x-h" ] && help
```

## double bracket [[ ]]

Bashになって [^bashadding] 追加された条件式で、`[ ]`の機能が拡張されたもの。
返す真偽値は同じだが、次の様な機能が追加されている。

```bash:bash
( <EXPRESSION> )					式のグループ化。優先される。
<EXPRESSION1> && <EXPRESSION2>		論理積。なお、従来の `-a` は使ってはいけない。
<EXPRESSION1> || <EXPRESSION2>		論理和。なお、従来の `-o` は使ってはいけない。
! <EXPRESSION1> 					論理否定。
<STRING> == <PATTERN>				文字列比較。
<STRING> = <PATTERN>				== と同じ。
<STRING> != <PATTERN>				文字列比較でマッチしていない場合に真。
<STRING> =~ <ERE>					拡張正規表現による文字列比較。
```

なお、正規表現を含む文字列比較は、`[ ]`と違って式中で対象文字列を""で囲まなくてよい [^3]が、Shellにより解釈されて意図しない文字列に変換される可能性がある。なので、直接`[[ ]]`中に正規表現を書くのではなく、変数を用いて比較すると良いだろう。

```bash:bash
REGEXP="[hc]at"
[[ cat =~ $REGEXP ]] && echo "Matched"
```

[^bashadding]: BashはBourne Again Shellで、Bourne Shell（いわゆる`sh`）から拡張されたシェルである。
[^3]: `[a-z]`など、ローケルに少なからず影響されるので、POSIXクラスを使うと良いだろう。 （cf; https://ja.wikipedia.org/wiki/%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE#%E6%A8%99%E6%BA%96 ）


## parentheses ( )

### サブシェル

丸かっこは、サブシェルで記述されたコマンドまたはコマンド群を実行する。
shからの機能である。

```bash:sh
pwd
( cd /usr; pwd )
pwd       # 最初の結果と同じ 
```

### コマンド実行

shで `` ` ` ``  で実現していたコマンド実行を、 Bashでは `$()` で表すことができる。
入れ子にもできるので便利。

```bash:bash
SEED=$(expr $(date +%M) + $(date +%S))
echo $SEED

echo `date '+%Y/%m/%d'`        # sh 方式も使える
```

### 関数

関数を宣言する場合に、関数名に()をつけて宣言する。

```bash:sh
funcname() {
    local FUGA
    set -- $*
    HOGE=$1
    FUGA=$2    # FUGA はローカル変数
}

# Call
funcname
funcname "hoge" "fuga"
```


## double parentheses (( ))

算術演算を表す。Cの形式の演算が使えたり[^csyntax]と、いろいろな式が使える。

```bash:bash
echo $(( 0x10 ))             # 16
echo $(( (2+3) * 5 / 2 ))    # 12. 小数は表せない
```

`if` の式として使えるが、`[ ]` による式と違って、値が`0`の時に偽値となる。

```bash:bash
if ((0)); then
  echo "true"
else
  echo "false"
fi
# "false"
```

Cのようなforループも、表すことが可能。

```bash:bash
for ((x=1; x<=3; x++))
{
  echo $x
}
# Bash では、 do~done の代わりに、{~} が使える
```

[^csyntax]: http://wiki.bash-hackers.org/syntax/arith_expr



## braces { }

### 変数展開

変数名と文字列を区別する時に、変数名を明示的に指し示すために使う。

```bash:sh
STR="user_${NAME}_logged_in!"
```

２桁以上の位置パラメータを表す時にも使う。

```bash:sh
$ piyopiyo.sh "hoge" "fuga" 3 4 5 6 7 8 9 ten eleven
# $1    => hoge
# ${10} => ten
```

Bash独自の便利な変数展開もある。[こちらの記事](http://qiita.com/bsdhack/items/597eb7daee4a8b3276ba)にまとめられている。

```bash:bash
echo $HOGE            # (null)
echo ${HOGE:-hoge}    # hoge    ... just behalf
echo $HOGE            # (null)

echo $FUGA            # (null)
echo ${FUGA:=fuga}    # fuga
echo $FUGA            # fuga    ... also substituting value as deafult
```

### コマンド群の実行

`()` がサブシェルでコマンドを実行するのに対し、カレントシェルで（ブロック処理のように）一連のプログラムを実行できる。
出力内容は、纏めてリダイレクトなどが可能。

```bash:sh
{
  echo "PASSWD follows"
  cat /etc/passwd
  echo
  echo "GROUPS follows"
  cat /etc/group
} >output.txt
```

シンプルな例としては、関数での使用例。

```bash:sh
help() {
    echo "Usage: $0 [-h|--help]"
    exit 255
}
```

[ここに乗っていた](http://wiki.bash-hackers.org/syntax/ccmd/grouping_plain) Try-Catch ブロックの例。

```bash:sh
try_catch() {
    { # Try-block:
        eval "$@"
    } ||
    { # Catch-block:
        echo "An error occurred"
        return -1
    }
}
```


## 参考

ここのサイトが大変参考になった！

Bash Hackers Wiki - http://wiki.bash-hackers.org/start