🐚
【Bash】ツール作りでよく使う機能チートシート
はじめに
- ミニツール作成で何度も書く機能があるため、自分用のメモとして作成しました。
スクリプト実行時のパスを変数に格納する
- スクリプト実行時、パスの変更が起こる前に、
CURRENT_DIR="${pwd}"
ルート権限で実行しているか確認する
if [ "$(id -u)" -ne 0 ]; then
echo "This script must be run as root. Please use sudo or switch to the root user."
exit 1
fi
-
lshwや/var/log以下のファイルを確認したいときなど、ルート権限がないと正常に動作しないときに使用
引数が足りない場合に処理を終了する
if [ $# -ne 2 ]; then
echo "Usage: $0 <path_1> <path_2>"
exit 1
fi
-
$0で起動中のシェルスクリプト名を出力できる
引数を変数に格納する
PATH_1="$1"
PATH_2="$2"
データを配列にマッピングする
例: 改行によって区切られたデータが入っている./exapmles.txtをマッピングする
mapfile -t CMDS < ./examples.txt
マッピング後の配列に対して順番に処理を実行する場合の書き方は以下のようになる
for cmd in "${CMDS[@]}"; do
...
done
処理が完了するのを待つ
同期処理の場合
cp ./something1.sh ./something2.sh &
wait
echo "copied"
- 子プロセスのexit後に次の処理を実行する書き方
- 以下のような処理(同期処理)の場合はこの書き方で処理完了を待てる
- ファイル操作(
cp,rmなど) - テキスト処理(
grep,sedなど) - パッケージインストールコマンド(
apt installなど) - 普通のシェル組み込み関数
- ファイル操作(
- 以下のような処理(同期処理)の場合はこの書き方で処理完了を待てる
- ほか、systemd serviceのうち
Type=oneshotと書かれているものはコマンドの終了 = 起動のメインプロセスの終了であるため、このコマンドで処理完了をキャッチできる
非同期処理の場合
until systemctl is-active --quiet something.service; do
sleep 1
done
- コマンドのexit = 処理完了ではないことがありうる場合は上記のように
untilやwhileを使う
TIMEOUT=30
while ! systemctl is-active --quiet something.service; do
if [ "$TIMEOUT" -le 0 ]; then
echo "Timeout."
exit 1
fi
sleep 1
(( TIMEOUT-- ))
done
- 一定時間でタイムアウトさせたい場合はこのように書く
日時情報を入れた文字列を生成する
timestamp=$(date +"%Y%m%d_%H%M%S")
log_file="logCollect_${timestamp}.log"
- ログファイルを各回別々に生成する時や1つのログファイルに追記していくときに使用
標準出力と同じものをログに書き込む
exec > >(tee -a "$log_file") 2>&1
- これを最初に書いておけば、ほぼ同内容のスクリプトを標準出力とログ出力で書き分ける必要がなくなり、コードが見やすくなる
使う例
exec > >(tee -a "./sample.txt") 2>&1
echo -e "\n--- System Information ---"
uname -a
cat /etc/os-release
...
使わない例
echo -e "Collecting system information..." | tee -a "$log_file"
system_info=$(uname -a)
echo "$system_info" | tee -a "$log_file"
os_release=$(cat /etc/os-release)
echo -e "OS release info:\n$os_release" | tee -a "$log_file"
...
エラーがあったらスクリプトを即終了する
set -euo pipefail
- uオプション: 未定義の変数を参照した際にエラーを発生させる
- eオプション: スクリプト内で終了コードが0以外になった(コマンドが失敗した)場合、即座にスクリプトを終了する
- oオプション + pipefailフラグ: pipeでつないだコマンドの終了ステータスを「最後のコマンドの終了ステータス」から「エラーになった最後のコマンドの終了ステータス」に変更する
- エラーになったコマンドがなければ0が返る
- 破壊的な変更を及ぼすスクリプトの場合や開発中に使える
エラーがあってもスクリプトを続行する
systemctl restart XXX || true
- 失敗した場合でも構わず続行していい場合(ログ収集が目的の場合など)やエラーハンドリング不要な箇所の場合に使える
ユーザに確認してから処理する
read -p "Do you want to proceed the reset? [y/N]: " ans
if [ "$ans" =~ ^[Yy]$ ]; then
echo "Proceeding..."
# 後続の処理
else
echo "Canceled."
fi
- pオプション: 質問文(後続の引用符部分)を画面に表示する
- 破壊的な変更をする前に出力するとユーザーフレンドリー
実行したコマンドのPIDを格納する
command_A &
A_PID=$! # command_AのPIDが格納される
kill $A_PID
スクリプトの終了時に特定の処理を行う
cleanup() {
...
}
trap cleanup SIGINT SIGTERM
参考
Discussion