シェルスクリプト その1

  • ShellScript
  • よく使う文法

    シェルスクリプトで頻繁に使用する文法をメモしておきます。

    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