よく使う文法
シェルスクリプトで頻繁に使用する文法をメモしておきます。
Current Directory
スクリプトが配置されているディレクトリから処理をしたいということはよくあると思います。そんな時はこれを使うと便利です。
sample.sh
#!/bin/bash
SCRIPT_DIR=$(cd $(dirname $0); pwd)
echo ${SCRIPT_DIR}
実行すると
[emily@emily 20200623]$ ./sample.sh
/home/emily/work/20200623
DateTime
スクリプトの中で日付も良く使いますね。以下は実行時の DateTime を取得するのに使います。
sample.sh
#!/bin/bash
DATETIME_1=`date +%Y%m%d%H%M%S`
echo ${DATETIME_1}
DATETIME_2=`date +%Y-%m-%d`
echo ${DATETIME_2}
DATETIME_3=`date "+%Y-%m-%d %H:%M:%S"`
# スペースがある場合、"" で囲む。
echo ${DATETIME_3}
実行すると
[emily@emily 20200623]$ ./sample.sh
DATETIME_1= 20200623101631
DATETIME_2= 2020-06-23
DATETIME_3= 2020-06-23 10:16:31
実行スクリプトのファイル名を取得・切り取り
たとえば、sample.sh を実行したときに、スクリプト名を使ってログファイルを生成したりすることがあります。
ログファイル名を "sample.log" としたいとき "sample.sh" の "sh" の部分が邪魔ですね。
シェルスクリプトでは簡単にできます。
file_name.txt
#!/bin/bash
SCRIPT_NAME=$(basename $0)
echo ${SCRIPT_NAME}
# そのまま
echo ${SCRIPT_NAME
%.*}
# 拡張子を削除
echo ${SCRIPT_NAME
##*.}
# 拡張子を取得
echo "test" >> ${SCRIPT_NAME%.*}.log
# "sample.log" というログファイルを作りたい。
実行すると
[emily@emily 20200623]$ ll
total 4
-rwxr-xr-x 1 emily emily 1565 Jun 23 12:18 sample.sh
[emily@emily 20200623]$ ./sample.sh
sample.sh
sample
sh
[emily@emily 20200623]$ ll
total 8
-rw-rw-r-- 1 emily emily 5 Jun 23 12:21 sample.log <-- できた!
-rwxr-xr-x 1 emily emily 1565 Jun 23 12:18 sample.sh
標準出力・エラー出力をログファイルにリダイレクトする
両方ログファイルに出力したいときは、魔法の言葉「2>&1」を使います。
その意味を見ていきます。
sample.sh
#!/bin/bash
SCRIPT_DIR=$(cd $(dirname $0); pwd)
SCRIPT_NAME=$(basename $0)
LOG_FILE=${SCRIPT_DIR}/${SCRIPT_NAME%.*}.log
ls sample.sh
1>> ${LOG_FILE}
ls aaaa
1>> ${LOG_FILE}
# "aaaa" は存在しないのでエラーとなる
実行すると
[emily@emily 20200623]$ ./sample.sh
ls: cannot access aaaa: No such file or directory
[emily@emily 20200623]$ cat sample.log
sample.sh
"ls aaaa" は失敗していますが、ログファイルに出力されていません。
では、この場合はどうなるでしょう。
sample.sh
#!/bin/bash
SCRIPT_DIR=$(cd $(dirname $0); pwd)
SCRIPT_NAME=$(basename $0)
LOG_FILE=${SCRIPT_DIR}/${SCRIPT_NAME%.*}.log
ls sample.sh
2>> ${LOG_FILE}
ls bbbb
2>> ${LOG_FILE}
# "bbbb" は存在しないのでエラーとなる
実行すると
[emily@emily 20200623]$ ./sample.sh
sample.sh
[emily@emily 20200623]$ cat sample.log
ls: cannot access bbbb: No such file or directory
今度は逆に "ls bbbb" が失敗したときの内容がログファイルに出ています。
一方で、成功したときの結果はログに出ていません。
こうして、魔法の言葉「2>&1」は開発されました。
sample.sh
#!/bin/bash
SCRIPT_DIR=$(cd $(dirname $0); pwd)
SCRIPT_NAME=$(basename $0)
LOG_FILE=${SCRIPT_DIR}/${SCRIPT_NAME%.*}.log
ls sample.sh >> ${LOG_FILE}
2>&1 # 2>&1 は後ろに
ls cccc >> ${LOG_FILE}
2>&1 # "cccc" は存在しないのでエラーとなる
実行すると
[emily@emily 20200623]$ ./sample.sh
[emily@emily 20200623]$ cat sample.log
sample.sh
ls: cannot access cccc: No such file or directory
これで標準出力も標準エラー出力もログに出せることが出来ました。
if もしも、~だったら、~してる
もしも、綺麗な女性に生まれていたら、お金持ちと結婚して、一生楽して暮らしたい。
そんな願望を実現するのが、if です。
if ファイルやディレクトリの存在有無
私が一番使うのはファイル・ディレクトリがあるかないかの分岐です。
sample.sh
#!/bin/bash
if [ ! -d "${SCRIPT_DIR}/log" ]; then
# ディレクトリが無いときは処理
mkdir ${SCRIPT_DIR}/log
fi
if [ ! -f "${SCRIPT_DIR}/sample.txt" ]; then
# ファイルが無いときは処理
touch ${SCRIPT_DIR}/sample.txt
fi
実行すると
[emily@emily 20200623]$ ll
total 4
-rwxr-xr-x 1 emily emily 423 Jun 23 10:28 sample.sh
[emily@emily 20200623]$
[emily@emily 20200623]$ ./sample.sh
[emily@emily 20200623]$
[emily@emily 20200623]$ ll
total 8
drwxrwxr-x 2 emily emily 4096 Jun 23 10:30 log
-rwxr-xr-x 1 emily emily 423 Jun 23 10:28 sample.sh
-rw-rw-r-- 1 emily emily 0 Jun 23 10:30 sample.txt
if 比較演算子 数字
比較する対象は数字です。同じか、大きいか、小さいか、で処理を分岐させます。
sample.sh
#!/bin/bash
count=5
if [ ${count} -gt 5 ]; then
echo "${count} は 5 より大きいです。"
else
echo "${count} は 5 以下です。"
fi
if [ ${count} = 5 ]; then
echo "${count} は 5 です。"
else
echo "${count} は 5 ではありません。"
fi
if [ ${count} -lt 5 ]; then
echo "${count} は 5 より小さいです。"
else
echo "${count} は 5 以上です。"
fi
実行すると
[emily@emily 20200623]$ ./sample.sh # count=5 で実行
5 は 5 以下です。
5 は 5 です。
5 は 5 以上です。
[emily@emily 20200623]$ ./sample.sh # count=4 で実行
4 は 5 以下です。
4 は 5 ではありません。
4 は 5 より小さいです。
[emily@emily 20200623]$ ./sample.sh # count=6 で実行
6 は 5 より大きいです。
6 は 5 ではありません。
6 は 5 以上です。
if 比較演算子 文字
比較する対象は文字です。同じか、違うか、含むか、含まないか、で処理を分岐させます。
私がこれまでに使用した場面で多かったのが、たとえば、AWS などのクラウドサービスの運用で、対象のインスタンスのステータスが running だったら shutdown する。
逆に、対象のインスタンスのステータスが stopped だったら start-up する。などですね。
何らかのステータスを受け取って、同じなら処理A、違ったら処理B、といった使い方が多い気がします。
sample.sh
#!/bin/bash
DB_INSTANCE=koizumi-postgresql-01
# AWS RDS の koizumi-postgresql-01 の DBInstanceStatus を DB_STATUS に代入
DB_STATUS=`aws rds describe-db-instances \
--db-instance-identifier ${DB_INSTANCE} \
| jq -r .DBInstances[].DBInstanceStatus`
if [ "${DB_STATUS}" == "available" ]; then
echo "DB_STATUS is ${DB_STATUS}"
elif [ "${DB_STATUS}" == "stopped" ]; then
echo "DB_STATUS is ${DB_STATUS}"
else
echo "DB_STATUS is ${DB_STATUS}"
fi
実行すると
[emily@emily 20200623]$ ./sample.sh # DB_INSTANCE=koizumi-postgresql-01 で実行
DB_STATUS is available
[emily@emily 20200623]$ ./sample.sh # DB_INSTANCE=koizumi-sqlserver で実行
DB_STATUS is stopped
[emily@emily 20200623]$ ./sample.sh # DB_INSTANCE=koizumi-db-01-instance-1 で実行
DB_STATUS is starting
if の注意点
比較演算子は、基本的に "数字" と "数字" の比較、"文字列" と "文字列" の比較しかできません。
そのため、変数に想定外の値が入らないようハンドリングしておく必要があります。
私は、if を使うときは最低限、 else で何かしら記述するようにしています。
ShellScript