ssh-keygen や sudo passwd など、対話的に応答を求めてくるコマンドは、通常のシェルスクリプトでは自動化が難しい。
sshpass は使えても、複数の質問に順番に答えるような複雑な対話は手に負えなかったりする。この記事では、対話型コマンドを自動化するためのツール
expect の実践的な使い方を解説する。基本構文から、SSHログイン自動化、パスワード変更スクリプト、タイムアウト処理まで、現場で実際に使えるパターンを紹介する。動作確認環境: RHEL 9.4 / Ubuntu 24.04 LTS
この記事のポイント
・expect は spawn(起動)・expect(待機)・send(入力)の3コマンドで構成される
・SSHのパスワード入力やsudo対話を完全無人化できるが、鍵認証の方が安全でシンプル
・spawn -noecho と log_user 0 でパスワードをターミナルに表示しないよう制御できる
・expect_background ではなく exp_continue でループを回すのが実践の定番パターン
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
expectとは何か(仕組みと使いどころ)
expect は Tcl(Tool Command Language)を拡張したスクリプト言語で、対話型プログラムとの「会話」を自動化するツールだ。通常のシェルスクリプトは「コマンドを実行してその結果を処理する」ことが得意だが、プログラムが「何かを要求してくるまで待つ」という対話的な動作は苦手だ。
expect はこの問題を解決する。主な用途はこのようなケースだ。
・SSHログイン時のパスワード入力を自動化する(鍵認証が使えない環境)
・
passwd や adduser の対話プロンプトに自動応答する・古いネットワーク機器へのtelnetログインを自動化する
・FTPクライアントのコマンド操作を自動化する
1. expectのインストール
# RHEL / AlmaLinux / Rocky Linux $ sudo dnf install expect # Ubuntu / Debian $ sudo apt install expect # バージョン確認 $ expect -v expect version 5.45.4
基本的な使い方(spawn・expect・send)
expectスクリプトは3つのコア命令で構成される。・spawn:自動化したいコマンドを子プロセスとして起動する
・expect:子プロセスの出力から特定の文字列が来るまで待機する
・send:子プロセスに文字列を送信する(
\r でEnterキーを表す)1. 最もシンプルなexpectスクリプト(helloworld)
#!/usr/bin/expect # 最小構成のexpectスクリプト # 子プロセスを起動する spawn /bin/bash # "$ " というプロンプトが来るまで待つ expect "$ " # コマンドを送信する(\r はEnterキー) send "echo 'Hello from expect'\r" # 再びプロンプトが来るまで待つ expect "$ " # スクリプトを終了する send "exit\r" expect eof
2. スクリプトに実行権限を付けて実行する
$ chmod +x hello.exp $ ./hello.exp spawn /bin/bash [server01 ~]$ echo 'Hello from expect' Hello from expect [server01 ~]$ exit
3. タイムアウトを設定する(timeout)
expect はデフォルト10秒待機する。応答が来ない場合の動作をコントロールするにはタイムアウト設定が必要だ。#!/usr/bin/expect # タイムアウトを30秒に設定する(デフォルト10秒) set timeout 30 spawn ssh user@192.168.1.100 # パスワードプロンプトが30秒以内に来ない場合はタイムアウト expect { "password:" { send "mypassword\r" } timeout { puts "タイムアウト: 接続に失敗しました" exit 1 } } expect "$ " send "hostname\r" expect "$ " send "exit\r" expect eof
SSHログインの自動化
1. パスワード認証でSSHログインして操作する
#!/usr/bin/expect # ssh-login.exp: SSHログインとコマンド実行を自動化する set host [lindex $argv 0] # 第1引数: ホスト名またはIPアドレス set user [lindex $argv 1] # 第2引数: ユーザー名 set password [lindex $argv 2] # 第3引数: パスワード(後述の安全対策を参照) set timeout 20 # SSH接続を起動する(known_hosts確認を自動でyes) spawn ssh -o StrictHostKeyChecking=no ${user}@${host} # パスワードプロンプトを待つ expect { "password:" { send "${password}\r" } "Are you sure you want to continue connecting" { send "yes\r" expect "password:" send "${password}\r" } timeout { puts "エラー: SSH接続タイムアウト" exit 1 } } # ログイン後のプロンプトを待つ expect "$ " # 実行するコマンド send "df -h\r" expect "$ " send "free -h\r" expect "$ " send "exit\r" expect eof puts "SSH操作が完了しました"
[local@client ~]$ ./ssh-login.exp 192.168.1.100 tomohiro 'P@ssw0rd' spawn ssh -o StrictHostKeyChecking=no tomohiro@192.168.1.100 tomohiro@192.168.1.100's password: Last login: Sun Jun 22 14:00:00 2026 from 192.168.1.50 [tomohiro@server01 ~]$ df -h Filesystem Size Used Avail Use% Mounted on /dev/sda1 50G 12G 38G 24% / ... SSH操作が完了しました
2. ホストフィンガープリントの確認をスキップする(注意点)
StrictHostKeyChecking=no はMITM攻撃のリスクを高めるため、本番環境では注意が必要だ。既知ホストへの接続なら、事前に ssh-keyscan で known_hosts に追加しておく方が安全だ。# 事前にknown_hostsに追加する $ ssh-keyscan -H 192.168.1.100 >> ~/.ssh/known_hosts # 追加後はStrictHostKeyChecking=noなしで接続できる spawn ssh ${user}@${host}
sudoを使ったroot権限コマンドの自動化
1. sudoパスワードを自動入力する
#!/usr/bin/expect # sudo-auto.exp: sudoの対話を自動化する set sudo_pass "mysudopassword" set timeout 15 spawn bash -c "sudo systemctl restart nginx" expect { "\[sudo\] password for" { send "${sudo_pass}\r" } "password:" { send "${sudo_pass}\r" } eof { # パスワード不要のsudoers設定の場合はここに到達 } timeout { puts "タイムアウト" exit 1 } } expect { eof { puts "nginxの再起動が完了しました" } timeout { puts "コマンド実行タイムアウト" exit 1 } }
passwdコマンドの自動化(パスワード変更)
1. ユーザーパスワードを自動設定する
#!/usr/bin/expect # change-passwd.exp: passwdコマンドの対話を自動化する set username [lindex $argv 0] set newpass [lindex $argv 1] set timeout 15 spawn sudo passwd $username expect "New password:" send "${newpass}\r" expect "Retype new password:" send "${newpass}\r" expect { "passwd: password updated successfully" { puts "パスワードの変更が完了しました" exit 0 } "BAD PASSWORD" { puts "エラー: パスワードがポリシーを満たしていません" exit 1 } timeout { puts "タイムアウト" exit 1 } }
# 実行例 $ ./change-passwd.exp testuser 'NewP@ss2026' spawn sudo passwd testuser New password: Retype new password: passwd: password updated successfully パスワードの変更が完了しました
exp_continueでループ処理を行う
複数の選択肢に対して繰り返し対応するにはexp_continue を使う。これはexpectブロックを再度実行する命令で、繰り返し出てくるプロンプトへの対応に使う。#!/usr/bin/expect # expect-loop.exp: 複数のプロンプトに順番に対応する set timeout 30 spawn ftp ftp.example.com expect { "Name " { send "anonymous\r" exp_continue } "Password:" { send "anonymous@example.com\r" exp_continue } "ftp> " { # ログイン成功 send "ls\r" } timeout { puts "接続タイムアウト" exit 1 } } expect "ftp> " send "quit\r" expect eof
セキュリティ上の注意点
expectはパスワードをスクリプト内に平文で記述するケースが多く、セキュリティリスクがある。本番環境での使用前に以下を必ず確認すること。・スクリプトのパーミッション:パスワードを含むスクリプトは
chmod 700 でオーナーのみ実行可能にする・環境変数でパスワードを渡す:スクリプト内にハードコードするより
$env(SSH_PASS) で参照する方がgitへの誤コミットを防げる・expect vs 鍵認証:SSHの自動化は原則として公開鍵認証を使うべきだ。expectによるパスワード自動化は鍵認証が使えない古いシステムや制約のある環境に限定する
# 環境変数でパスワードを渡す例(スクリプト側) set password $env(MY_SSH_PASS) # 実行側 $ export MY_SSH_PASS='P@ssw0rd' $ ./ssh-login.exp 192.168.1.100 tomohiro
トラブルシュート・よくある問題
1. expect: command not found
expectがインストールされていない。dnf install expect または apt install expect でインストールする。2. プロンプトが一致しない(ログイン後に止まる)
expect で待つ文字列がサーバー環境によって異なる場合に起きる。interact コマンドや log_user 1 で実際の出力を確認してプロンプト文字列を調整する。# デバッグ: 出力を全て表示する log_user 1 # 正規表現でプロンプトにマッチさせる(汎用的) expect -re "\\\$\s*$" expect -re "#\s*$"
3. パスワードがターミナルに表示される
send した文字列はデフォルトでターミナルにエコーされる。ログに残したくない場合は log_user 0 で出力を抑制する。expect "password:" log_user 0 # エコーを無効化 send "${password}\r" log_user 1 # エコーを元に戻す
本記事のまとめ
| やりたいこと | expectの書き方 |
|---|---|
| コマンドを子プロセスで起動する | spawn コマンド |
| 特定の文字列が来るまで待つ | expect "文字列" |
| 文字列を送信する(Enter込み) | send "入力\r" |
| 複数パターンに分岐する | expect { "A" { } "B" { } } |
| expectブロックを繰り返す | exp_continue |
| タイムアウトを設定する | set timeout 30 |
| 出力を画面に表示しない | log_user 0 |
| プロセス終了まで待つ | expect eof |
expect は「古いシステムとの対話を自動化する最後の手段」として現場で今も活躍する。鍵認証やAPIが使えるなら迷わずそちらを選ぶべきだが、レガシーなネットワーク機器や制約のある環境では expect が唯一の自動化手段になる。まずは最小構成の spawn → expect → send → expect eof パターンから始めてほしい。
Linux Master Pro Seminar(2日間ハンズオン)では AlmaLinux・Rocky Linux・RHEL対応の本番サーバー設計から運用まで、
実際のサーバーを触りながら習得できます。
>> セミナー詳細・お申込みはこちら
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら
- 次のページへ:iftopコマンドでLinuxのネットワーク通信をリアルタイム監視する方法|接続先・帯域の確認と実務活用も
- 前のページへ:socatコマンドでLinuxのネットワーク通信をデバッグする方法|ポートフォワード・SSL・ファイル転送の実践例
- この記事の属するカテゴリ:Linuxtips・シェルスクリプトへ戻る

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