Bashの括弧のノウハウ(まとめ)


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

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

bracket [ ]

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

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

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

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

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になって 3 追加された条件式で、[ ]の機能が拡張されたもの。
返す真偽値は同じだが、次の様な機能が追加されている。

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

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

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

parentheses ( )

サブシェル

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

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

コマンド実行

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

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

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

関数

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

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

# Call
funcname
funcname "hoge" "fuga"

double parentheses (( ))

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

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

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

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

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

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

braces { }

変数展開

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

sh
STR="user_${NAME}_logged_in!"

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

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

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

コマンド群の実行

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

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

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

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

ここに乗っていた Try-Catch ブロックの例。

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

参考

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

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


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

  2. /bin ディレクトリの中を確認してみよう 

  3. BashはBourne Again Shellで、Bourne Shell(いわゆるsh)から拡張されたシェルである。 

  4. [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 ) 

  5. http://wiki.bash-hackers.org/syntax/arith_expr