expectコマンドでscpを自動化(cronで実行)する

2013年8月2日

scpで別サーバにファイルを転送する際に、別サーバ側の認証が入る。
これをシェルスクリプトで行う場合、expectというコマンドを利用します。

expectで利用される主なコマンドは以下となります。

expect -c:ダブルクォートで囲った複数行を自動対話処理する
spawn:コマンドを実行する
expect:対話の受信
send:対話の送信
interact:対話の終了
"\r":リターンコード

シェルスクリプトを以下の様に作成します。
対話処理が複数回発生する場合は、それごとにexpectを記述してください。

#!/bin/sh

HOST=192.168.10.10
USER=ログインユーザ
PASS=パスワード
TARGET_FILE=/var/www/backup.tar.gz
BACKUP_DIR=/backup/

/usr/bin/expect -c "
set timeout 30
spawn /usr/bin/scp -p ${TARGET_FILE} ${USER}@${HOST}:${BACKUP_DIR}
        expect {
                \"password: \" {
                        send \"${PASS}\r\"
                }
        }
        interact
"

ここで手動でシェルスクリプトを実行すると、正常に別サーバに転送されるはずです。
しかし、これをcronにて自動的に実行すると、先程と同様な結果になりません。つまり転送が失敗します。
理由はcron自体がdaemonとして実行されているので「対話の終了」という認識がされないとか言われてます。

そこでシェルスクリプトを以下の様に書き直します。

#!/bin/sh

HOST=192.168.10.10
USER=ログインユーザ
PASS=パスワード
TARGET_FILE=/var/www/backup.tar.gz
BACKUP_DIR=/backup/

/usr/bin/expect -c "
set timeout 30
spawn /usr/bin/scp -p ${TARGET_FILE} ${USER}@${HOST}:${BACKUP_DIR}
        expect {
                \"password: \" {
                        send \"${PASS}\r\"
                }
        }
        expect {
                eof { exit 0}
        }

"

interactの代わりにeofで受けます。
この際、正常終了なら「exit 0」、異常終了なら「exit 1」とか返り値を与えることが出来ます。
書き換えたシェルスクリプトはcronでも手動でも実行可能となります。