基本
シェルスクリプト
条件分岐・繰り返し・関数・引数・変数展開など、bash スクリプトを書くための組み込みコマンドと書き方。
スクリプトの先頭には、どのシェルで動かすかを書きます。
#!/usr/bin/env bash
set -euo pipefailchmod +x script.sh で実行権限を付け、./script.sh で実行します。
test / [ ] / [[ ]]条件を調べる
ファイルがあるか、文字列や数値が等しいかなどの条件を調べます。if と組み合わせて使います。
if [ -f config.yml ]; then echo "ある"; fi
if [[ $name == lap* && -n $HOME ]]; then echo "一致"; fi # [[ ]] は bash/zsh の拡張版
[ -d build ] || mkdir build| 書き方 | 意味 |
|---|---|
-e path / -f path / -d path | 存在する / ファイル / ディレクトリ |
-r / -w / -x | 読める / 書ける / 実行できる |
-s file | サイズが0より大きい |
-L path | シンボリックリンク |
f1 -nt f2 | f1 の方が新しい |
-z "$s" / -n "$s" | 空 / 空でない |
"$a" = "$b" / "$a" != "$b" | 文字列が等しい / 違う |
$n -eq 1 / -ne / -lt / -le / -gt / -ge | 数値の比較 |
[[ $s =~ ^[0-9]+$ ]] | 正規表現に一致([[ ]] のみ) |
[[ $s == *.txt ]] | ワイルドカードに一致([[ ]] のみ) |
! 条件 / && / || | 否定 / かつ / または |
if / case条件分岐
条件によって処理を分けます。
if [ "$#" -eq 0 ]; then
echo "引数がありません" >&2
exit 1
elif [ "$1" = "-h" ]; then
usage
else
echo "引数: $1"
fi
case "$1" in
start|run) echo "起動" ;;
stop) echo "停止" ;;
*.txt) echo "テキストファイル" ;;
*) echo "不明: $1" ;;
esacfor / while / until繰り返し
同じ処理を繰り返します。ファイルを1つずつ処理したり、1行ずつ読んだりするのに使います。
for f in *.jpg; do echo "$f"; done
for i in {1..5}; do echo "$i"; done
for ((i = 0; i < 3; i++)); do echo "$i"; done
for host in web1 web2 db1; do ssh "$host" uptime; done
while read -r line; do # ファイルを1行ずつ読む
echo "行: $line"
done < list.txt
count=0
until [ $count -ge 3 ]; do count=$((count + 1)); done
while true; do date; sleep 5; done # 無限ループ(Ctrl+C で止める)break で抜け、continue で次の周回に進みます。
function / local / return関数を作る
よく使う処理に名前を付けてまとめます。
greet() {
local name="${1:-world}" # local で関数の中だけの変数にする
echo "hello, $name"
return 0 # 終了コード(0〜255)。値を返すには echo を使う
}
greet alice
msg=$(greet) # 出力を受け取るread入力を読む
キーボードからの入力や、ファイルの1行を変数に読み込みます。
$ read -r -p "名前: " name
$ read -s -p "パスワード: " pw; echo # 入力を表示しない
$ read -t 10 -p "10秒以内に: " ans # タイムアウト
$ IFS=, read -r a b c <<< "1,2,3" # 区切って変数に入れる
$ read -r -a arr <<< "x y z" # 配列に入れるshift / getopts引数を処理する
スクリプトに渡された引数やオプションを順に処理します。
#!/usr/bin/env bash
verbose=0; out=out.txt
while getopts "vo:h" opt; do
case $opt in
v) verbose=1 ;;
o) out=$OPTARG ;;
h) echo "使い方: $0 [-v] [-o 出力] ファイル..."; exit 0 ;;
*) exit 1 ;;
esac
done
shift $((OPTIND - 1)) # 処理したオプションを取り除く
echo "残りの引数: $@"| 変数 | 意味 |
|---|---|
$0 | スクリプトの名前 |
$1 〜 $9、${10} | 位置引数 |
$# | 引数の数 |
"$@" | すべての引数(1つずつクォートされる。基本はこちら) |
"$*" | すべての引数を1つの文字列に |
set / shoptシェルの動きを変える
エラーで止める、実行するコマンドを表示するなど、シェルの動きを変えます。
| 書き方 | 意味 |
|---|---|
set -e | コマンドが失敗したら止まる |
set -u | 未定義の変数を使ったらエラー |
set -o pipefail | パイプの途中の失敗も失敗とみなす |
set -x | 実行するコマンドを表示する(デバッグ) |
shopt -s globstar | ** でサブディレクトリまで一致させる |
shopt -s nullglob | 一致するファイルがないとき、パターンを空にする |
shopt -s dotglob | * に隠しファイルも含める |
trap終了時やシグナルで処理を走らせる
スクリプトが終わるときや中断されたときに、後片付けなどの処理を走らせます。
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT # 終わるとき(エラーでも)に片付ける
trap 'echo "中断しました"; exit 130' INTdeclare配列・連想配列・型付きの変数
配列や連想配列などの変数を作ります。
arr=(apple banana cherry)
echo "${arr[0]}" # apple
echo "${arr[@]}" # すべて
echo "${#arr[@]}" # 要素の数
arr+=(date) # 追加
for x in "${arr[@]}"; do echo "$x"; done
declare -A price # 連想配列
price[apple]=100
price[banana]=80
for k in "${!price[@]}"; do echo "$k=${price[$k]}"; done
declare -i n=5 # 整数
declare -r PI=3.14 # 読み取り専用mapfileファイルを配列に読み込む
ファイルの各行を配列に読み込みます。
$ mapfile -t lines < list.txt
$ echo "${#lines[@]} 行"
$ mapfile -t files < <(find . -name '*.md')変数の展開
| 書き方 | 意味 | 例(f=photo.tar.gz) |
|---|---|---|
${var:-既定} | 空なら既定値を使う | ${NAME:-guest} |
${var:=既定} | 空なら既定値を代入する | |
${var:?メッセージ} | 空ならエラーで止まる | ${1:?ファイルを指定} |
${#var} | 文字数 | ${#f} → 12 |
${var%.*} | 後ろから最短一致を削除 | photo.tar |
${var%%.*} | 後ろから最長一致を削除 | photo |
${var#*.} | 前から最短一致を削除 | tar.gz |
${var##*.} | 前から最長一致を削除(拡張子) | gz |
${var/old/new} | 最初の一致を置換 | |
${var//old/new} | すべて置換 | ${path// /_} |
${var:0:5} | 部分文字列 | photo |
${var^^} / ${var,,} | 大文字に / 小文字に |
let / (( ))整数の計算
整数の計算をします。
$ echo $((3 + 4 * 2)) # 11
$ n=5; ((n++)); echo $n # 6
$ (( n > 3 )) && echo "大きい"
$ echo $((16#ff)) # 16進数 → 255
$ echo $((2 ** 10)) # べき乗小数の計算は bc を使います。
exit / return終了コード
スクリプトや関数を終了し、成功・失敗を知らせます。
exit 0 # 成功
exit 1 # 失敗(1〜255 のどれか)直前のコマンドの終了コードは $? で分かります。cmd && echo 成功 || echo 失敗 のように使えます。
eval / execコマンドを組み立てて実行する
組み立てた文字列をコマンドとして実行したり、今のシェルを別のものに置き換えたりします。
$ cmd="ls -l"; eval "$cmd"
$ exec > >(tee -a run.log) 2>&1 # 以降の出力をすべてログにも残す
$ exec zsh # 今のシェルを zsh に置き換えるwaitバックグラウンドの処理を待つ
バックグラウンドで動かした処理が終わるのを待ちます。
for h in web1 web2 web3; do
ssh "$h" 'apt update' &
done
wait # すべて終わるまで待つ
echo "完了"sleep待つ
指定した時間だけ待ちます。
$ sleep 5 # 5秒
$ sleep 0.5 # 0.5秒
$ sleep 2m # 2分(s・m・h・d)true / false何もせず成功 / 失敗する
何もせず、成功または失敗を返します。
while true; do ...; done
$ false || echo "失敗したので実行"mktemp一時ファイルを安全に作る
一時ファイルや一時フォルダを、ほかと重ならない名前で安全に作ります。
$ tmp=$(mktemp) # /tmp/tmp.XXXXXXXX
$ dir=$(mktemp -d) # 一時ディレクトリ
$ mktemp --suffix=.jsonヒアドキュメント・プロセス置換
cat <<EOF # 変数を展開する
こんにちは $USER
EOF
cat <<'EOF' # 展開しない(そのまま)
$HOME はそのまま
EOF
cat <<-EOF # 行頭のタブを取り除く
インデントしても大丈夫
EOF
diff <(sort a.txt) <(sort b.txt) # コマンドの出力をファイルのように渡す
grep foo <<< "$text" # 文字列を入力に渡す(ヒアストリング)whiptail簡単なダイアログを出す
スクリプトから、端末上に選択肢や確認のダイアログを出せます。
$ whiptail --yesno "続けますか?" 8 40 && echo "はい"
$ name=$(whiptail --inputbox "名前は?" 8 40 3>&1 1>&2 2>&3)
$ whiptail --menu "選んでください" 15 50 3 1 "起動" 2 "停止" 3 "状態" 3>&1 1>&2 2>&3