linux  >  シェルスクリプトのメモ

シェルスクリプトのメモ

シェルスクリプトを書く上で、よく忘れるのでメモ

動作はCentOS5 or RHEL5+Bash(Bourne-Again Shell)で確認。

○行説明(わかりづらかったので色付け)
 スクリプト例 
 実行結果 

○色々なメモ

  • 1行目
    スクリプトファイルの1行目は、スクリプト内の行を実行するプログラムを識別するために、カーネルによって使用される。
    #!はマジックナンバーと呼ばれ、コメントではなく、実行環境の宣言になる。
    ここではBashを使用するので、 #!/bin/sh と記述する。
    ksh(korn shell)の場合なら #!/bin/ksh 等に置き換える。
     
  • デバッグ
    シェルスクリプトの
    文法エラー確認
    -nのオプションを付けてシェルスクリプトを実行
    エラーがない場合は何も表示されない。
    例:

    $ sh -n test.sh

  • シェルスクリプトのトレース

    -xのオプションを付けてシェルスクリプトを実行

    例:
    ○ test.sh(中身)

    export LANG=C
    echo "test"
    echo `date`

    ○ シェルスクリプト実行結果

    $ sh -x test.sh

    + export LANG=C
    + LANG=C
    + echo test
    test
    ++ date
    + echo Sat Jan 1 00:00:00 JST 2000
    Sat Jan 1 00:00:00 JST 2000

    ※ export LANG=Cをしてるのは、ターミナル上で文字化けの回避

    因みに、毎回トレースを入れるなら、マジックナンバー部分に
    #!/bin/sh -x
    としておけば普通に起動してもトレースモードになる。

 

○位置パラメータとコマンド行引数

  • 位置パラメータ数を取得

    $#

    例:

    ○test.sh (中身)

    #!/bin/sh
    echo $#


    ○結果(1)

    $ sh test.sh 0

    1

    ○結果(2)

    $ sh test.sh 0 1

    2

     

○条件分岐

  • if文

    if 条件
    then
        コマンド
    elif 条件
    then
        コマンド
    else
        コマンド
    fi

    例:入力されたパラメータの入力値調査

    val=$1
    if [ ${val} -lt 1 ]
    then
        echo "入力値は1より小さい"
    elif [ ${val} -eq 1 ]
    then
        echo "入力値は1"
    else
        echo "入力値は1より大きい"
    fi

    もちろん、elif 条件ぬきでも書くことはできる。

 

  • if文等使用時の演算子

    数値比較

    演算子 意味
    -eq == equal 同じ
    -ne != not equal 違う
    -lt < less than 未満(より小さい)
    -gt > greater than より大きい
    -le <= less equal than 以下
    -ge >= greater equal than 以上

    英語で覚えるとわかりやすい。

     

    文字列比較

    演算子 意味
    = 等しい
    != 等しくない

    (何故か先日ここでひっかかったので追加w)

    例:文字列が等しいか

    if [ ${str} = "test" ]
    then
            echo "ok"
    else
            echo "ng"
    fi


    ファイル属性

    演算子 意味
    -d ディレクトリなら真を返す
    -f 通常ファイルなら真を返す
    -L シンボリックリンクなら真を返す
    -r 読み取り可能ファイルなら真を返す
    -w 書き込み可能ファイルなら真を返す
    -x 実行可能ファイルなら真を返す
    -s サイズが0より大きければ真を返す

    例:カレントディレクトリ上に「test」ディレクトリが有るか

    if [ -d test ]
    then
            echo "ok"
    else
            echo "ng"
    fi

    ※否定の場合は!を付ける

    if [ ! -d test ]
    then
            echo "ok"
    else
            echo "ng"
    fi

 

  • 最後に実行したコマンドの終了ステータスを取得

    $?

    例:test.shというプログラムを走らせた結果で分岐

    ./test.sh
    if [ $? -eq 0 ]
    then
        exit "ok"
    else
        exit "ng"
    fi

  • バックグラウンドに送った最後のジョブのPID値を取得

    $!

    例:a.shというプログラムをバックグラウンドで走らせてa.shのPIDを取得

    ./a.sh > /dev/null 2>&1 &
    val=$!
    echo ${val}

  • シェルスクリプト(自プロセス)のPID値を取得

    $$

     

○囲い文字

  • 囲い文字

    シングル?ダブル?それともバッククォート?

    もち理解していますが、書いておきます

    上にも出てきていますが、関数dateをechoして結果を変数に保存したりするとき、そのまま打つと

    # echo date

    date

    そのまま出ちゃいます

    では順にシングル、ダブル、バックとやってみましょう

    ○シングルクォート

    # echo 'date'

    date

    ○ダブルクォート

    # echo "date"

    date

    ○バッククォート

    # echo `date`

    Sat Jan 1 00:00:00 JST 2000

    バッククォートの時が意図した処理になってあとは普通にdateという文字列表示です。

    では次に、シングルとダブルの違い

    変数に入れるのが面倒なのでLANG使わせてもらいましょう

    ○シングルクォート

    # echo '${LANG}'

    ${LANG}

    ○ダブルクォート

    # echo "${LANG}"

    ja_JP.UTF-8

    シングルの方は$変数を無視して表示です

    ここを使い分けられるようになるとシェルスクリプトの幅が広がるので覚えちゃいましょう~

    ※${LANG}と記載していますが、$LANGでもいいです。

 

○番外編。

(プチ実用(?)シェルスクリプトのサンプル)

※いかなる責任も負えませんので、使用は自己責任でお願いします。
※ダウンロード用サンプルスクリプトはUTF-8 LFで作成しています。

 

  • デーモン動作チェックスクリプト

    再起動直後ぐらいに実行すると良さそうなシェルスクリプト

    例: デーモン動作チェックプログラム
    原理は単純pidファイルを開いて、そのプロセスが生きているかどうか確認するだけ。
    ○proc_chk.sh (DL準備中。コピペしてくださいw)
    ※2010/06/22 更新

    #!/bin/sh

    function proc_chk()
    {
        RES=0
        chk_str=$1
        if [ -f $2 ]
        then
            pid_file=`cat $2|sed s/' '//g`
            if [ -d /proc/${pid_file} ]
            then
                RES=1
            else
                RES=0
            fi
        else
            RES=0
        fi

        printStr=${chk_str}'########################'

        STR_BUF=`echo ${printStr} | awk '{printf("%-20.20s",$1);}'|sed s/#/./g`
        if [ ${RES} -eq 1 ]
        then
                echo "${STR_BUF}[ OK ]"
        else
                echo "${STR_BUF}[ NG ]"
        fi
    }

    ########################
    #ここから下に調べたい物を記載。
    # proc_chk デーモン名(何でもいい) PIDファイル
    #Apache check
    proc_chk Apache "/var/run/httpd.pid"

    #ntpd
    proc_chk ntpd "/var/run/ntpd.pid"

    #sshd
    proc_chk sshd "/var/run/sshd.pid"

    ○結果

    # ./check_proc.sh
    Apache..............[ NG ]
    ntpd................[ OK ]
    sshd................[ OK ]

    ※Apacheが落ちている例

    個人的メモでは、awkの桁合わせ部分が覚えられないためのメモw
    プロポーショナルフォントだからずれて見えるけど、等幅フォントなら問題無し

    ※postfix引っかける場合sed使わないとダメかも。 2010/06/22版で対策済み
  • 拡張子一括変換スクリプト
    ○ext_chg.sh (DL準備中。コピペしてくださいw)

    #!/bin/sh

    #変更前拡張子
    ext=".TXT"
    #変更後拡張子
    chext=".txt"

    list=`ls`
    for file in ${list}
    do
        chfile=`echo ${file}|sed "s/${ext}/${chext}/g"`
        if [ ${file} != ${chfile} ]
        then
            mv ${file} ${chfile}
        fi
    done

  • 簡易CPU負荷チェックスクリプト
    ○loadaverage_chk.sh (DL準備中。コピペしてくださいw)
    ※現状では変数:min1のみ使用。min5とmin15は使っていません。
    ※crontabを使用してスクリプトを呼び出すと、定期監視になります。
    ※負荷が閾値を超えた場合メールがガンガン届くので注意してください。

    #!/bin/sh

    #-------------------------------------------------------------------------------------------------#
    #閾値
    threshold=1
    #宛先
    toaddr=root@localhost
    #送信元
    fromaddr=alertmail@localhost
    #-------------------------------------------------------------------------------------------------#

    loadaverage=`uptime | awk '{print $8 $9 $10;}' | tr "," " "`
    min1=`echo ${loadaverage} | cut -d" " -f1`
    min5=`echo ${loadaverage} | cut -d" " -f2`
    min15=`echo ${loadaverage} | cut -d" " -f3`

    comp=`echo "scale=2; ${threshold} < ${min1}" | bc`
    if [ ${comp} -eq 1 ]
    then
            mainmsg="サーバの負荷が閾値${threshold}を超え、${min1}になりました。"
            echo "${mainmsg}"
            echo "${mainmsg}" | /bin/mail -s "loadaverage alert" ${toaddr} -- -f ${fromaddr}
    fi

  • ftp通信スクリプト
    ○ftp_cron.sh (DL準備中。コピペしてくださいw)
    ※ざっくりと完成(2010/12/17 現在)
    時々"ftp -n"と言うプロセスが残ったままになるようなのでpidを吐いてkillするようにしたものです

    ※動作チェックはしましたが、運用チェックしてません。バグあったらごめんなさい。

    #!/bin/sh

    # initialize ------------------------------------------
    HOST_NAME="192.168.127.51"
    FTP_PID_PATH="./ftppid/"
    FTP_PID_FILE_NAME="ftp_`date +%Y%m%d%H%M%S`.pid"
    KILL_DAY=1
    # -----------------------------------------------------

    #PID DIR CHECK
    if [ ! -d ${FTP_PID_PATH} ]
    then
        mkdir ${FTP_PID_PATH}
    fi

    #FTP Connect
    ftp -n ${HOST_NAME} < ftpconnect.txt &

    #Write PID
    echo $! > "${FTP_PID_PATH}${FTP_PID_FILE_NAME}"

    #Delete PID file
    FIND_FILE=`find ${FTP_PID_PATH} -type f -print`
    for pidfile in ${FIND_FILE}
    do
            pid_res=`cat ${pidfile}`
        ps_res=`ps aux | grep ${pid_res} | grep ftp | grep -v grep | wc -l`
        if [ ${ps_res} -eq 0 ]
        then
            rm -f ${pidfile}
        fi
    done

    #Kill deadprocess
    FIND_FILE=`find ${FTP_PID_PATH} -type f -mtime +${KILL_DAY} -print`
    for pidfile in ${FIND_FILE}
    do
        kill `cat ${pidfile}`
        rm -f ${pidfile}
    done

    ○ftpconnect.txt (FTPの通信内容)

    user test password
    bin
    lcd /var/www/html/
    cd /ftproot/
    get test.txt
    bye

    ※補足
    接続ユーザ:test 、 パスワード:password で接続
    毎回PIDを吐いて2日前のプロセスをkill。プロセスチェックで実態の無いPIDファイルは削除

 

前
シェルの基礎
カテゴリートップ
Linux
次
シェルスクリプト:サンプル