この記事はQiitaで公開されていました
~
と*
を展開する場合はクオートしない
クオートされた文字列の中にある~
はそのまま文字として扱われる。扱いは*
と同じようにみえる。
$ echo ~/bin /Users/kadota/bin $ echo "~/bin" ~/bin $ echo *.txt hello.txt $ echo "*.txt" *.txt
$*
と$@
の違い
基本的には同じ動きをするが、クオートの中で使うと文字区切りの扱いが変わる。
# star.sh for i in $*; do echo "'$i'"; done # star-quote.sh for i in "$*"; do echo "'$i'"; done # atom.sh for i in $@; do echo "'$i'"; done # atom-quote.sh for i in "$@"; do echo "'$i'"; done
スペース($IFS
)を含む場合の例。
$ bash star.sh 1 "2 3" 4 '1' '2' '3' '4' $ bash atom.sh 1 "2 3" 4 '1' '2' '3' '4' $ bash star-quote.sh 1 "2 3" 4 '1 2 3 4' $ bash atom-quote.sh 1 "2 3" 4 '1' '2 3' '4'
間接参照
rcでは変数の値を変数名として使うと便利だけど
% a=foo % foo=1 % echo $$a 1
bashはそのまま実行すると、$$
が先に解決されてPIDになってしまう。
$ a=foo $ foo=1 $ echo $$a 1136a
正しくはこちら。
$ echo ${!a}
[
と[[
とtest
シェルで分岐をする場合によく使うこれ。[
はtest
と同じコマンドなのでどちらを使ってもいい。
# if test $a -gt 1でも同じ if [ $a -gt 1 ] then echo $a greater than 1 else echo $a less than or equal to 1 fi
だけどこの2つはシェルとは別のコマンドなので、空白文字や空文字の場合に注意しないといけなくてめんどくさい。
$ a="" $ [ $a = "" ] # unary operater expectedエラー $ [ "$a" = "" ] # ok $ a="1 2" $ [ $a = "" ] # too many argumentsエラー $ [ "$a" = "" ] # ok
bash
を使うのであれば、今は常に[[
を使うのが良いと思う。
$ a="" $ [[ $a = "" ]] $ a="1 2" $ [[ $a = "" ]]
正規表現でマッチ
text='a b c' if [[ $text =~ ^a.*$ ]] then echo match fi
ここでは、安易に正規表現をクオートしてはいけない。クオートすると、正規表現で特別な扱いされる文字が全てバックスラッシュでエスケープされる。シングルクオートとダブルクオートでどちらも同じ。
[[ $text =~ '^a.*$' ]] # \^a\.\*\$と同等
正規表現の途中でスペース等を含ませたい場合は、その部分だけクオートするとよい。
[[ $text =~ ^'a b'.*$ ]]
実際にどのようなエスケープがされるのかは、set -x
しておくとエスケープされた後の正規表現を見ることができる。
ifと&&
と||
ifの代わりに、&&
と||
が使える。エラー処理はこちらを使った方が読みやすい。
$ test -f file || echo 'file not exist' >&2 && exit 1
配列
簡単な使い方。
# 初期化 a=() # 末尾に追加 a=("${a[@]}" test) # 先頭に追加 a=(test "${a[@]}") # 内容を列挙 echo "${a[@]}"
単純にecho $a
すると、先頭の要素しか対象にならない。
配列に特定の要素が存在するか
専用の機能は無いが、[[
コマンドの=~
演算子が使える。
a=(cafe beef) if [[ " ${a[@]} " =~ " cafe " ]] then echo found fi
=~
は正規表現でマッチさせる演算子なので、スペースで要素の区切りを表現してあげなければならない。
計算する
$((10 + 1))
のように書くと外部コマンドなく計算ができる。
$ N=10 $ echo $((10 + $N)) 20 $ echo $((1 * 3 - 1)) 2
数字の前に基数を書ける。
$ echo $((16#0F + 8#10)) 23
((expr))
だけの場合は評価をするだけなので、条件式に使うと便利。
$ ((10 > 8#10)) && echo ok ok
trap
trap
は、SIGINT
, SIGHUP
などの他に以下の擬似シグナルが存在し、それらにハンドラを割り当てられる。
擬似シグナル | 意味 |
---|---|
ERR |
コマンド実行結果がエラーなら都度実行される |
EXIT |
コマンドを終了したときに実行される |
DEBUG |
コマンド実行のたびに都度実行される |
一時ファイルを必ず削除したい場合はEXIT
に登録しておくと良い。利用可能なシグナルはtrap -l
で表示できる。
変数展開
bash
は通常のshと比べてとても多くの展開式が使える。
変数置換
古くからある展開式。
書き方 | 意味 |
---|---|
${v:-text} |
vが未定義ならtextに置換 |
${v:=text} |
vが未定義ならtextをセットして置換 |
${v:?text} |
vが未定義ならtextを表示して終了 |
${v:+text} |
vが 定義されていたら textに置換 |
変数の少し変わった扱い方。
書き方 | 意味 |
---|---|
${#v} |
文字列の長さ、または配列の要素数に展開 |
${!v} |
vの値を変数名として展開 |
文字列置換
書き方 | 意味 |
---|---|
${v%glob} |
vの末尾から最短マッチした部分を削除 |
${v%%glob} |
vの末尾から最長マッチした部分を削除 |
${v#glob} |
vの先頭から最短マッチした部分を削除 |
${v##glob} |
vの先頭から最長マッチした部分を削除 |
${v/s1/s2} |
vに含まれるs1を1つだけs2に置換 |
${v//s1/s2} |
vに含まれるs1を全てs2に置換 |
bash 4以降から利用可能な式
便利な式だけど、macOS High Sierraはまだ3系なので利用箇所には注意。
書き方 | 意味 |
---|---|
${v^} |
先頭の文字を大文字にして展開 |
${v^^} |
全ての文字を大文字にして展開 |
${v,} |
先頭の文字を小文字にして展開 |
${v,,} |
全ての文字を小文字にして展開 |
${v~} |
先頭文字の大文字小文字を反転 |
${v~~} |
全ての文字で大文字小文字を反転 |
オプションを扱う
色々方法はあるけどgetopts
がいちばん面倒がなくて良いと思う。以下のコードでは、-fに引数がない・不明なオプションが与えられた時に、getopts
がエラーを出してくれる。
while getopts xf: OPT do case $OPT in f) filename="$OPTARG" ;; x) flagx=1 ;; esac done shift $((OPTIND - 1))
だけど不明なオプションでもエラーとしたくない場合は少しめんどくさい。getopts
最初の引数先頭に:
をつけるとエラーにならない代わりに、特殊なオプション文字として:
または?
のどちらかが入っている。
文字 | 意味 |
---|---|
? | 不明なオプション文字が使われた |
: | 引数が必要なオプションだけど無かった |
このように使う。
while getopts :xf: OPT do case $OPT in :) echo usage: $(basename $0) [-x] [-f file] >&2 exit 1 ;; x) flagx=1 ;; \?) x=$((OPTIND - 1)) echo unknown option: ${!x} >&2 exit 1 ;; esac done shift $((OPTIND - 1))
不明なオプションだった場合、具体的にどの文字が使われたのか調べる方法は特に用意されてなさそうだったので、上記のように自分で計算する。
先頭のアレ
シバン(shebang)というらしい。
#!/usr/bin/env bash
bashを使う場合、環境によってパスが違うかもしれないのでenvでラップするのをよく見かける。