シェルスクリプトってなに?

スピーカー: Takeshi@インフラ系の人

テーマ

お正月に作ったバックアップスクリプトについてLTを行います。 本勉強会で使用したバックアップスクリプト 。 ※ 提供して下さった「takeshi@インフラ系の人」様に感謝。

環境への接続

  • 秘密鍵を用いて勉強用のサーバに接続します。
  • @dozonotメモ:通常はID/PASSを用いてサーバに接続しますが、公開鍵認証を用いることでセキュリティが高まります。 → 手で入力できるパスワード(せいぜい十数桁)よりも、長い文字列を使って接続できるためです。
  • UNIX系OSからsshコマンドで接続する際には秘密鍵のパーミッションを変更する必要があります。
chmod 600 id_rsa
  • 環境に接続できたら、ホームディレクトリにスクリプトを配置するのでしばらく待つこと。
  • w もしくは finger コマンドで現在ログインしているユーザが分かる。
  • /var/log/secure にセキュリティ系のログが出力される。 悪いことをしたら怒られます。セッション切られます。
  • wallコマンド
send a message to everybody's terminal.
  • slコマンド
ゆたかさんが汽車を走らせてくれます。
  • touchコマンド ファイルのタイムスタンプを変更するコマンド。存在しないファイルを指定したらファイルを作成できる。
touch test.txt
  • teeコマンド 与えられた標準入力を標準出力に出しながら、同時に実行結果をファイルに書き込める。 「-a」オプションは追記モードの意味となる。
tee -a hoge.txt

もし「-a」オプションを付けなければ、上書きモードとなり既存のファイルの中身が消えてしまう。

  • ホームディレクトリにスクリプトを配置しています。 テキストエディタ(vimなど)でbackup.shを開きましょう。

これからシェルの中身を覗いていきます

  • シェル冒頭の #! は「shebang(シェバング)」 このスクリプトは、何で書いてますよーっていう宣言。コメントアウトではない。 スクリプトの1行目に以下のように指定する。
bashの場合 #!/bin/bash
pythonの場合 #!/usr/bin/python

「#!」はマジックナンバーというものであり、このファイルがインタプリタ的に動作するものとして認識される。(入門UNIXシェルプログラミング第5章環境より)

  • 本スクリプトの冒頭部分では、変数を定義している。 Bashでは、変数を定義するときと、変数にアクセスするときで、指定の方法が異なる。
変数定義 BKUPDIR1=/etc/vsftpd
変数にアクセス(中括弧は必須ではないが、付けたほうが分かりやすいのでおすすめ) ${BKUPDIR1}
  • 変数で指定するのは必須ではないが、スクリプトのメンテナンス性が向上する。
  • スクリプト内の「`(バッククォート)」で囲われたコマンドについて バッククォートで囲われたコマンドが実行されて、その実行結果がスクリプト内のに反映される。
BKUPFILE1=${BKUPSTORAGE}backup-`date +%Y%m%d`.backup_1.tar.gz

この例では、新たな変数「BKUPFILE1」に対して、事前定義していた変数「BKUPSTORAGE」の文字列と、「`date +%Y%m%d`」コマンドの実行結果を組み合わせて代入している。

  • bashのスクリプトでは、冒頭に#を入れることで、行末までをコメント扱いにできる。
  • BKUPFLAGFILE=${BKUPSTORAGE}Backup_Result.FLG について @dozonot 音声トラブルで聴き逃しました・・。どなたか補足をお願いします。 バックアップを実行した事を示すフラグファイル。 スクリプトの最後まで実行されると生成される。 このフラグファイルが作成されている状態で再度スクリプトが実行すると Full Backup ではなく Diff Backup の処理が実行される。(Diff Backup は未実装だそうです。) ↓のように if コマンドでフラグファイルの存在をチェックすることで条件分岐する。
if [ -f ${BKUPFLAGFILE} ]; then

このフラグファイルはこのスクリプト独自で使うものだが、同様の手法はよく使われるものらしい。 @pu_ri うろ覚えですが追記しました。

スクリプト本体を読み解く。

### Start of Exec Full Backup ### 以降でバックアップ処理が実装されている。

  • 次の行では、スクリプトの実行状況を標準出力(コンソール)に出力している。 こうすることで、処理の実行状況(進んでいるのか、思いだけなのか、停止しているのか)が推測できる。
# Get Backup File List Number #1 (About /etc). echo `date +%Y`"/"`date +%m`"/"`date +%d` `date +%H`":"`date +%M` "INFO" "Exec ls -lRa ${BKUPDIR1} > ${BKUPLISTLOG1} 2>&1" | tee -a ${BKUPRESULTLOG}
  • 次の行では、再帰的に全てのファイル/フォルダの一覧を取得している。 コマンド内にある「 > 」は、「左側」のコマンド結果の出力先を「右側」に変更している。(リダイレクト) 通常、lsコマンドは標準出力(コンソール)上に実行結果を返すが、「 > 」により、コマンドの実行結果が「${BKUPLISTLOG1}」に出力されるようになる。
ls -lRa ${BKUPDIR1} > ${BKUPLISTLOG1} 2>&1

「 > 」では、既存のファイルを消して、新しく実行結果を書き込む動作となる。 「 >> 」にすると、追記モードになる。既に存在しているファイルの末尾に実行結果を書き込む動作となる。 「 2>&1 」は、標準エラー出力を標準出力にマージしている。標準エラー出力の内容を標準出力と同じリダイレクト先に出力する際に使用する。

  • 次の行では、スクリプトの実行状況を出力している。
echo `date +%Y`"/"`date +%m`"/"`date +%d` `date +%H`":"`date +%M` "INFO" "Exec tar cvzf ${BKUPFILE1} ${BKUPDIR1}" | tee -a ${BKUPRESULTLOG}
  • コマンド内にある「 | (パイプ) 」は、「左側」のコマンド結果を「右側」のコマンドに引き渡している。コマンドの実行結果を基に、次のコマンドを実行したいときに使用する。
  • 次の行では、条件分岐を行っている。 bashでは「if」から「fi」までが一つのブロックとなる。 fiは「if」をひっくり返したものではなく..「Finish」かもしれない!!
if [ $? -eq 0 ]; then echo `date +%Y`"/"`date +%m`"/"`date +%d` `date +%H`":"`date +%M` "INFO" "Backup #1 End. Exit Status=0" | tee -a ${BKUPRESULTLOG} else echo `date +%Y`"/"`date +%m`"/"`date +%d` `date +%H`":"`date +%M` "ERROR" "Backup #1 Error Detected." | tee -a ${BKUPRESULTLOG} fi

?」は、コマンド実行時の終了ステータスを表す特殊変数。「?」は、コマンド実行時の終了ステータスを表す特殊変数。 「?」で直前のコマンドの実行ステータスを取得できる。 この例では、「$?」が「0(正常終了)」であることを条件に分岐している。