ShellCheckを使おう
以下のサイト/プログラムで、シェルスクリプトの文法をチェックしてくれます。
起動オプション
man bashしたあとすぐ先にあるOPTIONSを参照。
- ‘-c string’: コマンドを文字列から取得
- ‘-l’: ログインシェルとして実行
set
起動オプションには、setで指定できるオプションも使えます。
setはビルドインコマンドで、説明を見るには、man bashしたあと、
/^SHELL BUILTIN COMMANDSと入力するのが楽です。
- -e: exit 0以外になったときに停止
- -u: 未定義変数を使用した時に停止
- -x: 実行するコマンドを画面に出力する。
コマンドの結果代入は$(…)を使う
コマンドの結果を代入する方法にはバッククォートを使う方法と、
$(...)を使う方法がありますが、$(...)を使うようにした方がいいです。
その理由は以下に書かれています。
POSIX標準にも含まれており、bash, dash(Debian), busyboxなどでも使えるため、 互換性の問題もありません。
変数展開
以下は、次のシェルで確認しています。
- GNU bash 3.2.57(macOS High Sierra)
- Busybox 1.27.2-r7(Alpine Linux 3.7.0)
- Debian Almquist Shell(dash, Debian 9.4) ※一部のみ
未定義、空の場合の定義
- ${parameter:-word}: 未定義もしくはnullの場合、wordが使用される。
FOO=foo
echo ${FOO:-bar} # => foo
FOO=
echo ${FOO:-bar} # => bar
unset FOO
echo ${FOO:-bar} # => bar
- ${parameter:=word}: 未定義もしくはnullの場合、wordが使用され、代入される。
FOO=foo
echo ${FOO:=bar} # => foo
FOO=
echo ${FOO:=bar} # => bar
echo ${FOO}      # => bar
unset FOO
echo ${FOO:=bar} # => bar
echo ${FOO}      # => bar
- ${parameter:?word}: 未定義もしくはnullの場合、標準エラー出力にwordが出る。
FOO=foo
echo ${FOO:?foo is undefined} # => foo
echo $?                       # => 0
FOO=
echo ${FOO:?foo is undefined} # => FOO: foo is undefined
echo $?                       # => 1(bash, dash), 2(busybox)
unset FOO
echo ${FOO:?foo is undefined} # => FOO: foo is undefined
echo $?                       # => 1(bash, dash), 2(busybox)
- ${parameter:+word}: 未定義もしくはnullのときは何もせず、それ以外のときはwordに置き換えられる。- nullは空文字のことだと思いますが、なぜか置換されますね。。。
 
FOO=foo
echo ${FOO+bar} # => bar
FOO=
echo ${FOO+bar} # => bar
unset FOO
echo ${FOO+bar} # => (なし)
部分文字列
- ${parameter:offset}: offset文字目から後を出力する。
- ${parameter:offset:length}: offset文字目から後を出力する。
- 最初の文字は、offset=0となる。
- offsetに負の値を指定すると、後ろから取得する。ただし、コロンとマイナスの間にスペースを入れること。
FOO=foo.tar.gz
echo ${FOO:4}     # => tar.gz
echo ${FOO: -6}   # => tar.gz
echo ${FOO:4:3}   # => tar
echo ${FOO: -6:3} # => tar
単語の長さ
- ${#parameter}: 変数の長さを出力する。
FOO=foo.tar.gz
echo ${#FOO} # => 10
単語の削除
- ${parameter#word}: wordを前方から最短一致で削除する。
- ${parameter##word}: wordを前方から最長一致で削除する。
- ${parameter%word}: wordを後方から最短一致で削除する。
- ${parameter%%word}: wordを後方から最長一致で削除する。
FOO=foo.tar.gz
echo ${FOO#*.}  # => tar.gz
echo ${FOO##*.} # => gz
echo ${FOO%.*}  # => foo.tar
echo ${FOO%%.*} # => foo
単語の置換
- ${parameter/pattern/string}: 最長一致でpatternをstringで置き換える
FOO=foo.tar.tar.tar.gz
echo ${FOO/gz/bz}   # => foo.tar.tar.tar.bz
echo ${FOO/t*r/bar} # => foo.bar.gz
条件式
数値は「以上」「以下」などを使用するためアルファベット(eqなど)を使い、
文字列はその必要がないから、=を使うと覚えるといいかも。
(でも辞書順の比較はありますね)
- 条件: if, elif, else, fiifより)
- 論理演算子: and -> -a, or ->-o(testより)
- 文字列の比較(6.4 Bash Conditional Expressionsより)
- 等しい: =(推奨)または==- =が推奨なのはPOSIX準拠のため。
 
- 等しくない: !=
 
- 等しい: 
- 数値の比較: -eq,-ne,-lt,-le,-gt,-ge(6.4 Bash Conditional Expressionsより)
- -e file: ファイルが存在すればtrue
case
以下のような感じで書きます。
- *): defaultに相当
- ;;: breakに相当
- 終わりはesac(caseの逆)
case $VAL in
  a | b)
    ...
    ;;
  c | d)
    ...
    ;;
  *)
    ...
    ;;
esac
引数
引数の解析
ビルドインコマンドにgetoptsがある(未調査)。
引数をまとめる(join)
例えば引数を全てハイフン区切りにまとめるには以下のようにするといいです。
KEY=$(echo -n "$*" | tr ' ' '-')
変数が定義されているかどうかのチェック
以下のようにすればよい。この場合、VALが未定義の場合は '-' 以降、すなわち空文字になる。
if [ "${VAL-}" = "" ]
set -u してるときに変数が未定義または空文字かチェックする for RHEL/CentOS 6 - Qiita
「シェルスクリプト基本リファレンス」のp187に説明がある。
- ${パラメータ:-値}: パラメータが未定義もしくは空の場合、指定された値に展開される。
- ${パラメータ-値}: パラメータが未定義の場合、指定された値に展開される。
自分自身のパスに移動
# change to topdir
cd `dirname $0`
古い環境での注意点
3系列のときは、配列が使えなかったはず。
コマンドでエラーが出ても続行
bash スクリプトの先頭によく書く記述のおさらい | Money Forward Engineers’ Blog
command || true