Bashシェルスクリプティング¶
スタイルガイド¶
シェルスクリプトのスタイルガイドの参照
Google Shell Style Guilde
https://google.github.io/styleguide/shellguide.html
シェル構文¶
for文¶
数値範囲¶
~$ for value in {0..10}; do echo $value; done 0 1 : 10 ~$
~$ for suffix in {01..04}; do echo $suffix; done 01 02 03 04 ~$
カウンタによる制御¶
for (( i=0; i < $max; i++ )); do
echo $i
done
- 丸括弧は2重指定
if文¶
条件の記述¶
[]で条件を記述 | [[]]で条件を記述 |
---|---|
if [ "$x" -gt 0 ]; then echo x is plus fi |
if [[ $x -gt 0 ]]; then echo x is plus fi |
[]はコマンド * 変数参照をダブルクォートで囲わないと、変数未定義の時エラー |
[[]] は構文 |
bashでは、[[]] を使う方が応用範囲が広いのでおすすめらしいです。
複数条件¶
if [[ $x -gt 0 && $x -lt 10 ]]; then
[[]]の中でAND条件(&&)、OR条件(||)を記述できます。
正規表現¶
if [[ fruit =~ (apple|banana|cacao) ]]; then
=~ で正規表現のマッチを記述できます。
コマンドの終了ステータスを判定¶
if command; then ...
のように、角括弧なしに記載できます。
パイプの使用も可能です。if cmd1 | cmd 2; then ...
失敗(0以外)を真とする場合は、if ! cmd1; then ...
と記述します。
case文¶
関数¶
関数の定義と呼び出し¶
引数がない場合¶
関数の定義 | 関数の呼び出し |
---|---|
|
|
引数がある場合¶
関数の定義 | 関数の呼び出し |
---|---|
|
|
関数の中では、引数を$1, $2, ...
で参照します。
関数の呼び出しでは、関数名の後に、引数を空白区切りで列挙します。
関数の戻り値¶
標準出力を受け取る方法と、コマンド実行結果として値を受け取る方法があります。
- 関数内で標準出力した文字列を戻り値として受け取ることができます。
- 数値(0-255)を関数の戻り値として返却することができます。return文で値を返却、関数呼び出し側は、コマンド実行結果と同じく
$?
で値を取得します。
標準出力¶
関数の定義 | 関数の呼び出し |
---|---|
|
|
コマンド実行結果¶
関数の定義 | 関数の呼び出し |
---|---|
|
|
数値計算¶
算術式展開 $((算術式))¶
算術式を解釈、計算し、計算結果に置き換えてコマンドを実行します。
HOURS_OF_DAY=24
echo $((160 / HOURS_OF_DAY))
算術式の中で、変数は$なしで使用できます。整数同士の除算結果は小数点以下切り捨てで整数となります。(160.0と小数点表記の算術式を使うと小数点の結果が得られる)
算術式評価 ((算術式))¶
HOURS_OF_DAY=24
((DAYS = 160 / HOURS_OF_DAY))
ファイルに関する操作¶
パス名からファイル名を取り出し(最後の'/'より以降の文字列)¶
basename¶
~$ basename /path/to/alfa.omega alfa.omega ~$
xargsでbasenameを使用しても意図した振る舞いとならない¶
~$ ls foo 1.dat 2.dat 3.dat ~$ find foo | xargs basename 1.dat
xargsで複数の引数をbasenameに渡しても、basenameが1つしか引数を取らないためです。
代替手段は、
~$ find foo | awk -F/ '{print $NR}'
~$ find foo -printf '%f\n'
文字列置換¶
前方一致最長マッチ
~$ f=/path/to/alfa.omega ~$ echo ${f##*/} alfa.omega ~$
パス名からディレクトリ名を取り出し(最後の'/'の前までの文字列)¶
dirname¶
~$ dirname /path/to/alfa.omega /path/to ~$
文字列置換¶
後方一致最短マッチ
~$ f=/path/to/alfa.omega ~$ echo ${f%/*} /path/to ~$
パス名から拡張子を取り出し(最後の'.'より後ろの文字列)¶
~$ f=/path/to/alfa.omega ~$ echo ${f##*.} omega ~$
パス名から拡張子を除いたファイル名(基底名)を取り出し¶
~$ f=/path/to/alfa.omega ~$ basename $f .omega alfa ~$
ファイルの存在¶
実行可能なファイルの存在¶
[[ -x file ]]
ファイルの存在(ディレクトリは除外)¶
[[ -f file ]]
ディレクトリの存在¶
[[ -d directory ]]
ファイルまたはディレクトリの存在¶
[[ -e path ]]
ワイルドカードで指定したファイルの存在¶
- ls でのワイルドカードは、該当ファイルが多数の時に Argument list too long が出る可能性がある
compgen -G "~/work/*.txt"
を使うのが良さそう
if compgen -G "~/work/*.txt" > /dev/null; then
echo "files are exist"
fi
ディレクトリ操作¶
実行スクリプトのディレクトリを取得¶
$(cd $(dirname $0); pwd)
- サブシェルで実行するので、続くシェルスクリプトのカレントディレクトリは元のまま
ディレクトリの存在を確認¶
if [[ -d $dir ]]; then echo "$dir is a directory." else echo "$dir is NOT a directory." fi
日時¶
日付・時刻の取得¶
dateコマンド¶
今の日時を指定の書式で出力します。
date <+書式>
記号 | 意味 |
---|---|
%Y | 西暦4桁 |
%m | 月2桁 (01-12) |
%d | 日2桁 (01-31) |
%H | 時2桁 (00-23) |
%M | 分2桁 (00-59) |
%S | 秒2桁 (00-59) |
%s | UNIX時刻の秒(1970年1月1日0時UTCからの経過秒数) |
日付・時刻の計算¶
T.B.D.
文字列¶
一致¶
正規表現¶
if [[ "ABCXYZ" =~ ^[A-Z]*Z$ ]];
- 左辺に文字列、=~ で右辺に正規表現式を記述すると、正規表現に一致したかどうかの評価が得られる
データ構造¶
連想配列¶
連想配列の宣言¶
書式declare -A 配列名
使い方¶
declare -A maxDate maxDate[January]=31 maxDate[February]=28
コマンドライン¶
コマンドライン引数の取り込み¶
コマンドライン引数は、$1, $2, ...
に格納されます。
コマンドライン引数が幾つ渡されたかを確認するには、$#
を参照します。
if [[ $# != 2 ]]; then echo Usage: $0 <src> <dst> exit 1 fi cp -p $1 $2
コンソール出力¶
標準出力と標準エラー出力¶
通常のメッセージは標準出力を使用し、エラーメッセージは標準エラー出力を使用します。
エラー出力の例
echo "Error in processing" >&2
表示制御¶
スピナーのような表示¶
コンソールのある箇所で、くるくると一定周期で文字を変えて、スピナー表示をします。
function spinner() {
local spinner_chars="/-\|"
for (( i=0; i<${#spinner_chars}; i++ )); do
sleep 1
echo -en "${spinner_chars:$i:1}" "\r"
done
}
- spinner_charsに、一定周期で表示させたい文字を羅列
- spinner_charsに定義した文字数を ${#spinner_chars} で取得し、for文で文字数だけループ
- 周期を1秒とする(sleep 1)、小数点指定もできるので、もっと早い周期で回したいときは、sleep 0.5のように指定
- echo -en で、エスケープ文字を有効に(\nや\rなど)、-nで改行なしとし、
- 表示する文字を変数から切り出し。${変数名:オフセット:長さ}の書式で指定、ループ変数でオフセットを変化し毎回文字を変更
このspinnerは、1周で終わるので、適宜繰り返し呼び出します。
権限¶
実行権限¶
root権限で実行されているかを判定¶
if [[ ${EUID}:-${UID}} != 0 ]]; then echo "root accessibility is required for running this script" fi
書き方¶
コマンドスクリプト¶
コマンドラインから実行するスクリプトの記載例。主にGoogle Shell Style Guideに準拠して記述します。
- ファイル名は、スネークケース(小文字+アンダースコア)
- インデントは、空白2文字(TABは使用しない)
- 桁数は1行80文字
関数を定義してモジュール性のあるコードを記述します。
スクリプトの使い方(コマンドラインインタフェース)を説明するusage関数を定義し、ヘルプまたは誤ったコマンドラインオプションが指定されたときに使い方を表示します。
main関数を定義し、スクリプトを実行したときに最初に呼ばれる関数とします。
変数はグローバルとローカル(関数内)変数を意識し、なるべく不変とします。
sample_command.sh
|
シェバンは、bashを明示的に指定 |
|
エラー耐性の向上 -e: 実行したコマンドがエラーの場合中断 -u:未定義変数を使うとエラー -o pipefail: パイプの全てのコマンドを確認(-eはパイプの最後のコマンドのみ確認) |
|
スクリプト全体の定数はreadonlyを付けて全て大文字のスネークケース(screaming snake case) |
|
スクリプト全体の変数は小文字のスネークケース |
|
関数名は小文字のスネークケース。 コマンドの使い方を標準出力する関数。複数行に渡る出力なのでヒアドキュメントを使うのが定番。 |
|
関数コメントは、先頭に処理概要、Globals:に使用するグローバル変数、Arguments:に引数の有無とある場合はそれらの説明を記述。 標準/エラー出力を行う場合はOutputs:に記述、リターン値がある場合Returns:に記述。 関数ローカルな変数は、localをつけて小文字スネークケース |
|
main関数を定義、コマンドラインを引数に取る。スクリプトが実行されるとmain関数を呼び出す。 |
問題解決¶
T.B.D.
参考文献¶
ブログ¶