パスワードを打ち込んでログインして、ファイルをputして、接続を閉じる。1回2分の作業でも、1日10回やれば20分の消費だ。cronで深夜に自動実行したくても、パスワード入力が必要では実現できない。
この記事では、SSH鍵認証とSFTPのバッチモードを組み合わせて、シェルスクリプトからSFTPファイル転送を完全自動化する設計を解説する。アップロード・ダウンロードの実装パターンから、エラー検知の落とし穴、ログ設計、cronとの組み合わせまで実践コード付きで紹介する。動作確認環境はRHEL 9.4 / Ubuntu 24.04 LTS。
この記事のポイント
・SSH鍵認証でパスワード不要のSFTP自動接続を確立できる
・sftp -b でバッチファイルを渡せばput/getを無人実行できる
・sftpはファイル未存在でもexit 0を返すので戻り値だけに頼らない設計が必要
・ログとcronを組み合わせれば深夜の定期転送が実現できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
SFTPをシェルスクリプトで自動化する前に知っておくこと
SFTPはSSHプロトコル上で動くファイル転送サブシステムだ。FTPと名前が似ているが別物で、通信は全てSSHセッション内で暗号化される。シェルスクリプトからSFTP転送を自動化するには、次の2つが前提になる。
・SSH鍵認証:パスワード入力なしでSFTP接続できる状態にする
・バッチモード(-b オプション):コマンドをファイルに記述してsftpに渡す
対話モード(sftpを起動してコマンドを打つ)ではスクリプト自動実行ができない。バッチモードにすることで、cronやシェルスクリプトから呼び出せるようになる。
なお、SFTPはデフォルトでSSHポート(22番)を使う。ポート番号の開放状況を確認したい場合は Linux ポート確認の全コマンドが参考になる。
SSH鍵認証のセットアップ(パスワードなし接続の準備)
自動化の第一歩はSSH鍵認証の確立だ。鍵認証が通れば、以降のSFTP接続でパスワードを求められなくなる。1. SSH鍵ペアを生成する
転送元サーバー(スクリプトを動かすサーバー)で鍵ペアを生成する。パスフレーズなし(Enterキーのみ)で作成することで、無人実行が可能になる。# 転送元サーバーで実行 $ ssh-keygen -t ed25519 -f ~/.ssh/sftp_key -N "" Generating public/private ed25519 key pair. Your identification has been saved in /home/operator/.ssh/sftp_key Your public key has been saved in /home/operator/.ssh/sftp_key.pub The key fingerprint is: SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx operator@transfer01
2. 公開鍵をSFTPサーバーへ登録する
生成した公開鍵(.pub)を接続先SFTPサーバーの authorized_keys に追記する。# 転送先SFTPサーバーのユーザーホームに公開鍵をコピー $ ssh-copy-id -i ~/.ssh/sftp_key.pub -p 22 sftp-user@192.168.1.50 # または手動でauthorized_keysに追記(ssh-copy-idが使えない環境) $ cat ~/.ssh/sftp_key.pub | ssh sftp-user@192.168.1.50 "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
3. 接続確認
鍵認証でSFTPに接続できるか確認する。パスワードを一切求めずに接続できれば成功だ。# 鍵を指定してSFTP接続テスト(パスワードなしで繋がるか確認) $ sftp -i ~/.ssh/sftp_key sftp-user@192.168.1.50 Connected to 192.168.1.50. sftp> pwd Remote working directory: /home/sftp-user sftp> exit
SFTPバッチモードで自動転送スクリプトを作る
SSH鍵認証が確立できたら、バッチファイルを使った自動転送の実装に入る。1. SFTPバッチファイルの基本
sftp コマンドに -b オプションでコマンドファイルを渡すと、対話なしに実行できる。バッチファイルには sftp の対話コマンドをそのまま書く。# バッチファイルの例(/tmp/sftp_batch.txt) cd /remote/upload put /local/data/report_20260829.csv bye
# バッチファイルを指定してsftp実行 $ sftp -i ~/.ssh/sftp_key -b /tmp/sftp_batch.txt sftp-user@192.168.1.50 Uploading /local/data/report_20260829.csv to /remote/upload/report_20260829.csv /local/data/report_20260829.csv 100% 48KB 1.2MB/s 00:00
2. アップロードスクリプトの実装
実際の運用に使えるアップロードスクリプトの例を示す。転送するファイルをループで処理し、全件完了後に一時ファイルを削除する設計にする。#!/bin/bash set -euo pipefail SFTP_HOST="192.168.1.50" SFTP_USER="sftp-user" SFTP_KEY="/home/operator/.ssh/sftp_key" REMOTE_DIR="/remote/upload" LOCAL_DIR="/var/transfer/outbox" LOG_FILE="/var/log/sftp_upload.log" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "${LOG_FILE}" } # 転送対象ファイルを確認 FILES=("${LOCAL_DIR}"/*.csv) if [[ ! -e "${FILES[0]}" ]]; then log "転送対象ファイルなし。終了します" exit 0 fi # バッチファイルを一時生成 BATCH_FILE=$(mktemp /tmp/sftp_batch.XXXXXX) trap 'rm -f "${BATCH_FILE}"' EXIT { echo "cd ${REMOTE_DIR}" for f in "${FILES[@]}"; do echo "put ${f}" done echo "bye" } > "${BATCH_FILE}" # SFTP実行 log "アップロード開始: ${#FILES[@]}件" if sftp -i "${SFTP_KEY}" -b "${BATCH_FILE}" "${SFTP_USER}@${SFTP_HOST}" >> "${LOG_FILE}" 2>&1; then log "アップロード完了: ${#FILES[@]}件" else log "ERROR: sftpコマンドがエラーを返しました" exit 1 fi
3. ダウンロードスクリプトの実装
リモートから特定パターンのファイルを取得するケースの実装例だ。#!/bin/bash set -euo pipefail SFTP_HOST="192.168.1.50" SFTP_USER="sftp-user" SFTP_KEY="/home/operator/.ssh/sftp_key" REMOTE_DIR="/remote/download" LOCAL_DIR="/var/transfer/inbox" LOG_FILE="/var/log/sftp_download.log" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "${LOG_FILE}" } BATCH_FILE=$(mktemp /tmp/sftp_batch.XXXXXX) trap 'rm -f "${BATCH_FILE}"' EXIT { echo "cd ${REMOTE_DIR}" echo "lcd ${LOCAL_DIR}" echo "mget *.csv" echo "bye" } > "${BATCH_FILE}" log "ダウンロード開始" if sftp -i "${SFTP_KEY}" -b "${BATCH_FILE}" "${SFTP_USER}@${SFTP_HOST}" >> "${LOG_FILE}" 2>&1; then log "ダウンロード完了" else log "ERROR: sftpコマンドがエラーを返しました" exit 1 fi
エラー処理とログ設計
1. sftpの終了コードの落とし穴と対策
sftpコマンドには注意すべき挙動がある。put対象ファイルが存在しない場合でも、sftp は exit 0 を返すことがあるのだ。つまり、終了コードだけを見ていると「転送失敗が成功と判定されてしまう」という問題が起きる。この問題を防ぐには、転送後にリモートで ls を実行してファイルが存在することを確認する方法が有効だ。
# 転送後にリモートのファイル存在を確認する例 VERIFY_BATCH=$(mktemp /tmp/sftp_verify.XXXXXX) trap 'rm -f "${VERIFY_BATCH}"' EXIT TARGET_FILE="report_$(date '+%Y%m%d').csv" { echo "cd ${REMOTE_DIR}" echo "ls ${TARGET_FILE}" echo "bye" } > "${VERIFY_BATCH}" if sftp -i "${SFTP_KEY}" -b "${VERIFY_BATCH}" "${SFTP_USER}@${SFTP_HOST}" 2>&1 | grep -q "${TARGET_FILE}"; then log "検証OK: ${TARGET_FILE} をリモートで確認" else log "ERROR: リモートにファイルが存在しない" exit 1 fi
2. ログ出力と転送件数の記録
長期運用ではログが命綱になる。転送件数・開始時刻・終了時刻・ファイル名を記録しておくと、トラブル発生時の調査が圧倒的に速くなる。# ログ関数(タイムスタンプ付きでファイルと標準出力に二重出力) LOG_FILE="/var/log/sftp_transfer.log" log() { local level="$1" shift echo "[$(date '+%Y-%m-%d %H:%M:%S')] [${level}] $*" | tee -a "${LOG_FILE}" } # 使用例 log "INFO" "転送開始: ${COUNT}件" log "ERROR" "接続失敗: ${SFTP_HOST}" log "INFO" "転送完了: 所要時間 ${ELAPSED}秒"
cronで定期自動転送を組み込む
スクリプトが完成したら、cronで定期実行に組み込む。crontab の設定例を示す。# crontab -e で編集(operator ユーザー) # PATH と HOME を明示しないとスクリプト内のコマンドが見つからないことがある PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOME=/home/operator # 毎日 02:30 にアップロードを実行 30 2 * * * /home/operator/scripts/sftp_upload.sh # 毎時5分にダウンロードを確認 5 * * * * /home/operator/scripts/sftp_download.sh
スクリプト自体は実行ビットを立てておくこと。
$ chmod 700 /home/operator/scripts/sftp_upload.sh $ chmod 700 /home/operator/scripts/sftp_download.sh
「Permission denied」「No such file or directory」が出た時の対処法
「Permission denied (publickey)」が出る場合SSH鍵認証の設定に問題がある。以下を順番に確認する。
・SFTPサーバー側の ~/.ssh/authorized_keys のパーミッションが 600 になっているか
・~/.ssh ディレクトリ自体のパーミッションが 700 になっているか
・sftp_key.pub の内容が authorized_keys に正確にコピーされているか(余分な改行や空白が入っていないか)
・/etc/ssh/sshd_config で PubkeyAuthentication yes になっているか
接続の詳細ログを確認するには sftp に -vvv オプションを付けて実行する。
# 詳細ログ出力で認証フローを確認する $ sftp -vvv -i ~/.ssh/sftp_key sftp-user@192.168.1.50
リモートの移動先ディレクトリが存在しない可能性が高い。バッチファイルの cd コマンドより前に、mkdir コマンドを追加して必要なディレクトリを作成する。
# バッチファイルの先頭でディレクトリを作成しておく -mkdir /remote/upload cd /remote/upload put /local/data/file.csv bye
まとめ
SFTPファイル転送のシェルスクリプト自動化を設計するポイントをまとめる。| やりたいこと | コマンド・設定 |
|---|---|
| SSH鍵ペア生成(パスフレーズなし) | ssh-keygen -t ed25519 -f ~/.ssh/sftp_key -N "" |
| 公開鍵をリモートへ登録 | ssh-copy-id -i ~/.ssh/sftp_key.pub sftp-user@HOST |
| バッチモードでSFTP実行 | sftp -i ~/.ssh/sftp_key -b batch.txt user@HOST |
| 一時バッチファイル生成 | mktemp /tmp/sftp_batch.XXXXXX |
| 一時ファイルの自動削除 | trap 'rm -f "${BATCH}"' EXIT |
| バッチ内でディレクトリ作成(エラー無視) | バッチファイルに -mkdir /remote/dir と記述(エラー無視) |
| 複数ファイルのアップロード | put /local/path/file.csv(ループで複数行生成) |
| ワイルドカードでダウンロード | mget *.csv |
| 接続詳細ログ確認 | sftp -vvv -i KEY user@HOST |
SSH鍵認証の確立 → バッチファイルの生成 → sftp実行 → 事後検証 → ログ記録という一連の流れをシェルスクリプトにまとめれば、cronによる深夜の無人転送が実現できる。
シェルスクリプト講座を見る >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:Gitリポジトリをシェルスクリプトから操作する設計|自動pull・差分検知・タグ付与を安全に実装する方法
- この記事の属するカテゴリ:シェルスクリプトへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます