expectでrsyncのパスワード入力を自動化するのにいろいろハマった話
正月から何をやっているんだという感じですが、年末年始の間にNASをセットアップしてバックアップジョブの仕込みをしておりました
今回使ったのはQNAPのNASですが、rsyncはHybrid Backup Sync 3というアプリケーションの機能として提供されているようで、好きなようにrsyncdの設定を記述できないようです
管理画面を見ると、ネットワーク経由でのアクセスにはユーザー名とパスワード設定が必須となっていました
今回やりたかったバックアップ処理はUbuntuマシンで録画し続けているカメラ映像を、動画ファイルコピー用のbashスクリプトをcron登録することで定期実行させるというものです
rsyncでは対話式でパスワードを聞いてきますので、これをexpectを使って自動入力させます
まずはexpectをインストールします
sudo apt install expect
インストールが完了したら、/path/to/sorce/ディレクトリ内の前月分動画ファイル(2024-12-ddTHH-MM-DD.mp4みたいな名前のファイル)を一括でバックアップするスクリプトを作成していきます
まずは完成形から
#!/bin/bash # target_monthは前月とする target_month=$(date -d "$(TZ=UTC-9 date '+%Y%m01') 1 month ago" "+%Y-%m") include_file_name=${target_month}-*.mp4 syncpass=rsync_password # 転送:NASのIPが192.168.0.8の場合(destだけクォートで括っているのは実際の転送先ディレクトリ名にスペースが入っていたため) expect -c " set timeout -1 spawn rsync -ahvn --include \"${include_file_name}\" --exclude \"*\" /path/to/sorce/ \"rsync://rsync_user_name@192.168.0.8:/path/to/dest/\" expect \"Password: \" send \"${syncpass}\n\" expect eof "
※本記事のrsyncコマンドには不慮の事故を防ぐためすべてnオプションをつけています
と、さらっと完成形を書きましたが、ここに行きつくまでにいろいろとハマりました
実行完了するが実行されない
まずはexpectで実行する際の記法を紹介しているサイトを見ながら書いていったのですが、参考にしていたのはssh接続等の記事が多く、最後の `expect eof` が抜けている記事がほとんどでした
パスワード入力は通っているように見えるものの何も起こらないという現象に見舞われ、結構長時間ハマることになりました
このあたりの要否はspawnするコマンドにもよるものと思われます(無しで動いてくれても良いと思う…)
ファイル単位のソース指定がうまくいかない
rsync単体なら問題ないのにexpectを使うとうまくいかないケースが3点ほどありまして、その一つがこれです
rsync -ahvn /path/to/sorce/2024-12-*.mp4 "rsync://rsync_user_name@192.168.0.8:/path/to/dest/"
は通るのに、expectでspawnするとうまくいかず
rsync: [sender] link_stat “/path/to/sorce/2024-12-*.mp4” failed: No such file or directory (2)
というエラーが出ます
もちろん/path/to/sorce/2024-12-*.mp4のファイルはあります
このケースは別の書き方として–includeを使うことで回避しました
ただし、rsyncのincludeオプションには癖があり、解説してくれているサイトも多いので詳細は割愛しますが、includeしたあとにexcludeで*を指定しておかないと関係ないものを除外してくれず全件コピーとなってしまいます
オプション指定の仕方の微妙な違い
続いてハマったのはこれです
rsync -ahvn --include="2024-12-*.mp4" --exclude="*" /path/to/sorce/ "rsync://rsync_user_name@192.168.0.8:/path/to/dest/"
これも単体であれば問題なく動作するが、expectでspawnすると全ファイルが抽出されてしまいます
最終的にはincludeオプションの癖を利用して–exclude=”*”を最初に持ってくるという検証で全件が抽出されたので、excludeが効いてなさげ??ということで気づきましたが、最初は何が起こっているのはわからずこれもかなりの時間ハマっていました
解決法としては、イコールなしでのオプション指定でうまく動作しました
rsync -ahvn --include "2024-12-*.mp4" --exclude "*" /path/to/sorce/ "rsync://rsync_user_name@192.168.0.8:/path/to/dest/"
まさかこんなにいろいろハマるとは思わず、正月早々何やってるんだ!?と自分で突っ込みたくなるほど時間を溶かしてしまいました
そしてたぶんですが、rsyncのaオプションを付与しているものの、NASとはユーザーも異なるのでオーナー情報は同期されていないものと思われます
これによって何が起こるかというと、毎回対象ファイルが全件差分として抽出されます
何度か上書きしても問題なかったため、今回は深く考えずそのままにしました
dry-runではなく実際の転送時に途中で止まる
当初はdry-runオプションをつけて確認をしていて、これでOKと思い走らせてみたところ中途半端なところで止まってしまいました
どうやらexpectのタイムアウト値がデフォルトでは10秒とのことで、先頭に `set timeout -1` をつけてタイムアウトしないようにしました