novaの日記: えくすぺくと
expect を利用して、cron 経由で scp コマンドを発行する際のメモです。
本来なら expect なんぞ使わずに、パスフレーズ無しの鍵を作るべきなのでしょうが、
送り先のサーバが鍵交換に対応してないので。私の権限が及ぶモノでは無いので、設定変えられないし。
まずは expect が動かないとハナシになりません。fedora は default でインストールされていますが、
今回使う RHEL ES 4 にはインストールされていません。up2date で入れようかと思ったのですが……
そういや認証が必要なんだっけ。……資料探すのメンドイのでソースで入れよう。
うにっくすさんの覚え書き - Expect - インストール を参考にインストールしました。
expect を使用する際には、使いたいコマンドがどんなメッセージでどんな入力を要求するのか事前に調べておく必要が有ります。
「この文字列が現れたらコレを送れ」という処理の繰り返しになりますので。
手動で scp を実行した場合は以下のような対応になります。
初めての接続の時:
# scp ./zzz.utf nova@172.16.5.10:/home/nova
The authenticity of host '172.16.5.10 (172.16.5.10)' can't be established.
RSA key fingerprint is **:**:**:**:**:**:**:**:**:**:**:**:**:**:**:**.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '172.16.5.10' (RSA) to the list of known hosts.
nova@172.16.5.10's password:
zzz.utf 100% 115 0.1KB/s 00:00
二回目以降の接続の時:
# scp ./zzz.utf nova@172.16.5.10:/home/nova
nova@172.16.5.10's password:
zzz.utf 100% 115 0.1KB/s 00:00
以上の結果から、必要な処理は以下の二点。
- 「Are you sure you want to continue connecting (yes/no)?」って聞かれたら、「yes」と答える。
- 「password:」って聞かれたら、パスワードを入力。
コレを expect に答えさせれば自動化出来るワケですね。 google 先生に聞いて、以下のシェルスクリプトを書きました。 nova アカウントのパスワードは "hahaha" と仮定します。
#!/bin/sh
expect -c "
spawn scp /home/nova/zzz.utf nova@172.16.5.10:/home/nova
expect {
\"Are you sure you want to continue connecting (yes/no)?\" {
send \"yes\r\"
expect \"password:\"
send \"hahaha\r\"
} \"password:\" {
send \"hahaha\r\"
}
}
interact
"
実行してみると……ちゃんと送れますね。cron に仕込んでみると……送れない。 ……やっぱパクって切り貼りじゃダメか。 Manpage of EXPECT を読んで、各コマンドの役割を確認。
- spawn
コマンドを実行するプロセスを生成する。
- expect
プロセスの出力をパターンマッチングする。
- send
文字列をプロセスに送る。
- interact
プロセスの制御をユーザに渡す。
……プロセスの制御をユーザに渡す?それは端末に制御を引き継がせるというコト?
……そりゃ cron では使えないわな。誰も引き継いでくれないもの。
んで、改良したのがコレ。
#!/bin/sh
expect -c "
spawn scp /home/nova/zzz.utf nova@172.16.5.10:/home/nova
expect {
\"Are you sure you want to continue connecting (yes/no)?\" {
send \"yes\r\"
expect \"password:\"
send \"hahaha\r\"
} \"password:\" {
send \"hahaha\r\"
}
}
expect {
\"denied\" { exit 0 } \
\"100%\" { exit 1 }
}
"
パスワードの入力後に更に判定を入れました。コレで「denied」か「100%」が出るまで続きます。
転送中は転送率が表示されるので、この条件なら転送終了時に "exit 1" を実行して抜けます。
尚、当然ですがこの条件ではファイル名に「100%」って文字列を含んではイケマセン。
ファイル名が表示された瞬間に抜けますので。「denied」もダメだぞ。
認証に失敗した場合は「denied」にマッチし、"exit 0" を実行して抜けます。
因みに手動での認証失敗時の対応はこんな感じ。
# scp ./zzz.utf nova@172.16.5.10:/home/nova
nova@172.16.5.10's password:
Permission denied, please try again.
nova@172.16.5.10's password:
やっとこ動くようになったので、1.2GB のファイルでテスト。…………途中で止まるんですが?
何で?「denied」も「100%」も出てないダロ?……ログを取ろう。
log_file コマンドでログが取れます。書式は "log_file [ログファイル名]" です。因みに常に追記モードです。
…………「denied」も「100%」も出てないナ。何で落ちるんだろ?
因みに戻り値は 0 だが……「denied」にマッチしてるとは思えない。
「denied」にマッチした時は "exit 2" を実行するようにしてみると……やっぱ 0 だ。
「denied」も「100%」もマッチしてない。
…………兎に角、片っ端から調べるしかないな。
………………分かりました。spawn で生成されるプロセスの寿命が default で 20 秒なんです。
……そんなのアリですか。まぁ、無尽蔵にプロセスを増殖させない為の処置なんでしょうケド。
プロセスの寿命は、"set timeout [生かしたい秒数]" で指定出来ます。
そんなワケで最終的には以下の形に成りました。
#! /bin/sh
item=/home/nova/zzz.utf
targetPath=@172.16.5.10:/home/nova
user=nova
passwd=hahaha
alive_time=3600
logFile=/home/nova/scp.log
echo `date` >> $logFile
expect -c "
log_file $logFile
set timeout $alive_time
spawn scp $item $user$targetPath
sleep 1
expect {
\"Are you sure you want to continue connecting (yes/no)?\" {
send \"yes\r\"
expect \"password:\"
send \"$passwd\r\"
} \"password:\" {
send \"$passwd\r\"
}
}
expect {
\"denied\" { exit 2 } \
\"100%\" { exit 1 }
}
"
ret=$?
if [ $ret -eq 0 ]
then
echo "abort" >> $logFile
else
if [ $ret -eq 1 ]
then
echo "done" >> $logFile
else
if [ $ret -eq 2 ]
then
echo "fail" >> $logFile
fi
fi
fi
exit $ret
……転送が一時間で終わらないとアボートしますがね。 "set timeout -1" で不死身のプロセスを作るコトも出来るんですが……それもビミョー。 要件に因りますが、戻り値見て異常が有ればアラートメールでも送れば十分でしょう。
えくすぺくと More ログイン