Open12
Shell Script (Bash) メモ
よく使うおまじない
#!/usr/bin/env bash
set -euo pipefail
# set -x
export LC_ALL=C
-
#!/usr/bin/env bash
- Shebangで明示的に
bash
を指定 - ディストリにより
sh
がbash
とは限らないため
- Shebangで明示的に
-
set -e
- 非ゼロでコマンドが終了したら強制終了
-
set -u
- 未定義の変数が参照されたら強制終了
-
set -o pipefail
- パイプの途中でも非ゼロでコマンドが終了したら強制終了
-
export LC_ALL=C
- Cロケールに設定
スクリプトを置いてあるディレクトリにcdしてそのパスを保存する
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ファイルをリストアップして配列に代入する。プロセス置換を利用
mapfile -t FILES < <(find . -name foo -type f)
Globを使うのが手っ取り早いが、マッチするファイルが無い場合にパターンがそのまま FILES
に代入されてしまう。この挙動を避けるには shopt -s nullglob
を使うと良い
FILES=(foo*)
エラーが発生した行を表示する
trap 'on_error $LINENO $BASH_COMMAND' ERR
on_error() {
eecho_red "[ERROR] Unexpected error at line $1, command=$2, status=$?"
}
思ったより難しそうだった
標準エラー出力にechoする関数
eecho() { echo "$@" 1>&2; }
標準エラー出力に赤色でechoする関数
eecho_red() { local args=("$@"); eecho -e "${args[@]: -2:1}" "\e[31m${args[*]: -1}\e[m"; }
grep, awk, sed は GNU版とBSD版でオプションが異なる。ローカルのMacで動いていたのがCI上のLinuxで動かないみたいなことが起きる。そこでPerlを使う。
- perl as grep
perl -nle 'print "$1" if /^(\w.*):/' .github/paths-filter.yml
- perl as awk
perl -anle 'print " $F[0] = \"$F[1]\""' .tool-versions
- perl as sed
perl -i -pe "s/=\s*\"(.*)\"/= \"\"/g" versions.auto.tfvars
usage をヒアドキュメントで表示
usage() {
cat 1>&2 <<EOF
Description:
Awesome script
Usage:
bash $(basename "$0") --env <env> [--profile <profile>] <arg1> <arg2>
Arguments
arg1 : Argument 1
arg2 : Argument 2
Options:
--env : Environment name
--profile : Profile name
EOF
スクリプト終了時にお掃除する
trap 'cleanup "${COPIED[@]}"' EXIT
cleanup() {
for f in "$@"; do
rm "$f"
done
}
Bashだとこれで以下のケース全てカバーできる。
- 正常終了時
- SIGERR
- 異常終了時
- SIGINT
- Ctrl + C が押された時
- SIGTERM
-
kill
された時
-
ただし他のシェルでは挙動が異なる場合があるので注意。
Glob系オプション
shopt -s nullglob dotglob extglob
- nullglob
- Globの結果が空の場合にちゃんと空のリストになってくれる
- 何かデメリットあってデフォのおまじないから外した記憶があるんだが何だったけな…
- dotglob
- Globの結果にドットから始まるファイルを入れてくれる
- extglob
- ちょっと機能が増えたGlobが使える
ary[@]
と ary[*]
の違い
基本 [@]
で良い。配列をログに出力したい場合は [*]
を使う
文字列置換
配列が文字列を含むか否か
ary=(aaa bbb ccc)
if [[ " ${ary[*]:-} " =~ \ aaa\ ]]; then
echo "matched"
fi