expect コマンドの使い方|SSH 自動ログインと対話処理を自動化するスクリプト例

  • URLをコピーしました!
目次

はじめに

Linux 環境の運用では、コマンド実行中にパスワード入力を求められたり、確認プロンプト(Yes/No)で処理が一時停止したりする「対話処理」が、バッチスクリプトによる自動化の障壁になります。この対話を自動応答させるためのツールが expect です。本記事では Ubuntu 環境での基本的な使い方から、実務で頻出する SSH 自動ログインのスクリプト例、現行 OpenSSH への対応、そして安全な代替手段との使い分けまでを解説します。

この記事でわかること
  • expect が対話処理を自動化する仕組みと 4 つの主要コマンド
  • Ubuntu でのインストール手順とバージョン確認方法
  • SSH 自動ログインスクリプトの作成例と、現行 OpenSSH プロンプトへの対応
  • 平文パスワードのリスクと、SSH 公開鍵認証・sshpass との使い分け

expect は、システムの出力を監視して事前に定義した文字列を自動送信することで、SSH 接続や sudo などの対話処理をスクリプト化するツールです。Ubuntu では sudo apt install expect で導入でき、spawn・expect・send・interact の 4 コマンドで SSH 自動ログインを構成できます。ただしパスワードを平文で扱う性質上、サーバー間の自動化では SSH 公開鍵認証を優先し、expect は鍵非対応のレガシー機器などに用途を絞る使い分けが現実的です。

expect コマンドとは ── 対話処理を自動化する仕組み

Linux の標準的なシェルスクリプト(bash など)は、記述されたコマンドを上から順に実行する直列処理を得意とします。しかし、途中でユーザーからの入力を待機する処理(プロンプト)が発生すると、そこで処理がブロックされます。

コマンドラインにおける「対話(プロンプト待機)」の課題

例えば ssh コマンドでリモートサーバーに接続する際や、sudo コマンドを実行する際、システムはパスワード入力を求めて一時停止します。これらのコマンドは、標準入力(パイプやリダイレクト)からパスワードを受け付けない仕様になっていることが多いため、単純なシェルスクリプトでは自動化が困難です。

expect が自動応答を実現する仕組み

expect は、このような対話処理を自動化するために設計されたプログラムです。仕組みはシンプルで、「システムが出力する文字列を監視(expect)」し、あらかじめ設定した特定の文字列(password: など)を検知すると、「事前に定義した文字列を送信(send)」します。この「出力の監視」と「入力の送信」を組み合わせることで、人間がキーボードで応答しているかのように、対話処理をスクリプト上で完結させられます。

公式マニュアルでも、まず 4 つの中核コマンドから理解することが案内されています。

参考: Expect マニュアルページ(tcl-lang.org)
“start by reading the descriptions of spawn, send, expect, and interact”
(spawn、send、expect、interact の説明から読み始めるとよい)
https://www.tcl-lang.org/man/expect5.31/expect.1.html

expect のインストールと基本構文

Ubuntu / Debian 系 Linux では、標準のパッケージマネージャー(apt)から expect をインストールできます。expect の安定版は 5.45.4(2018 年公開)で、長期にわたり仕様は安定しています。

Ubuntu / Debian 環境へのインストール手順

# パッケージリストの更新
sudo apt update

# expect のインストール
sudo apt install expect

インストール後、expect -v でバージョン情報が表示されれば準備完了です(-v はバージョンを表示して終了するオプションです)。

中核となる 4 つの主要コマンド

expect スクリプトの中核となるコマンドは以下の 4 つです。これらの組み合わせで、多くの対話処理を自動化できます。

  • spawn: 自動化したいコマンド(ssh や ftp など)を、expect の管理下で子プロセスとして起動する。
  • expect: システムからの出力(プロンプトなど)を監視し、指定した文字列にマッチするまで待機する。
  • send: マッチした文字列に対して、応答として文字列(パスワードなど)を送信する。
  • interact: expect による自動処理を終了し、人間によるキーボード操作に制御を戻す。

send で文字列を送る際は、文末に改行を含めます。端末(pty)相手の対話では、\n よりも復帰文字 \r を用いるほうが安定して動作します。

参考: Expect マニュアルページ(man7.org)
“A return character is denoted \r.”
(復帰文字は \r で表される)
https://man7.org/linux/man-pages/man1/expect.1.html

expect を使った SSH 自動ログインスクリプト

インフラ運用で需要の高い SSH 接続の自動化を例に、具体的なスクリプトの作成手順を解説します。

SSH 自動接続スクリプトの作成例

リモートサーバーに SSH 接続し、パスワード入力を自動で行ったうえで、そのままターミナル操作を続行できるスクリプト(auto_ssh.sh)の例です。

#!/usr/bin/expect

# 変数の定義(環境に合わせて変更してください)
set HOST "192.168.1.100"
set USER "admin"
set PASS "P@ssw0rd123"

# タイムアウトの設定(秒)
set timeout 10

# SSH 接続コマンドの起動
spawn ssh ${USER}@${HOST}

# システムからの応答を監視し、分岐処理を行う
expect {
    # 初回接続時のホストキー確認(yes/no/[fingerprint])が出た場合
    "Are you sure you want to continue connecting" {
        send "yes\r"
        exp_continue
    }
    # パスワード入力プロンプトが出た場合(大文字小文字を区別しない)
    -nocase "password:" {
        send "${PASS}\r"
    }
    # タイムアウトした場合のエラー処理
    timeout {
        puts "接続がタイムアウトしました。"
        exit 1
    }
}

# 自動ログイン完了後、ユーザーのキーボード操作に切り替える
interact

スクリプトの処理フローと変数展開

シェバン(#!/usr/bin/expect)は、このスクリプトが bash ではなく expect で実行されることを宣言します。

変数の定義(set)では、接続先の IP アドレス、ユーザー名、パスワードを変数として定義し、後続処理で ${変数名} として展開します。接続先が変わってもスクリプトの修正が容易になります。

分岐処理(expect {})では、初回接続時の確認プロンプトと通常の password: プロンプトの両方に対応できるよう、波括弧内でパターンマッチングを行います。exp_continue は、マッチ後に再度 expect の監視を継続させるためのコマンドです。

旧来の記述からの変更点は次の通りです。

  • 送信文字列の改行を \n から \r に統一し、端末相手での安定性を高めています。
  • ホストキー確認のパターンを Are you sure you want to continue connecting までに短縮し、現行 OpenSSH の (yes/no/[fingerprint]) 形式でも前方一致で拾えるようにしています。
  • パスワードのパターンに -nocase を付与し、Password:password: の表記差を吸収しています。

手動操作への移行(interact)は、パスワード送信後にログインした状態で制御を人間に戻します。このコマンドがないと、ログイン成功と同時にスクリプトが終了し、接続が切断されます。

ホストキー確認を簡素化する別解

初回接続時の yes/no 分岐をスクリプトから省きたい場合、OpenSSH 7.6 以降で使える StrictHostKeyChecking=accept-newspawn の ssh オプションに付与する方法があります。

# 未知のホストは known_hosts に追加し、yes/no プロンプトを出さない
spawn ssh -o StrictHostKeyChecking=accept-new ${USER}@${HOST}

このオプションは、未知のホスト鍵を自動で受け入れて known_hosts に追加する一方、既知ホストの鍵が変化した場合は接続を拒否します。StrictHostKeyChecking=no のようにすべての検証を無効化するわけではないため、自動化と最低限の安全性を両立しやすい選択肢です。ただし初回接続時の中間者攻撃を完全に防ぐものではないため、信頼できるネットワーク内での利用にとどめることをおすすめします。

参考: OpenSSH ssh_config マニュアル
“accept-new” mode automatically adds new host keys to known_hosts.
(accept-new は新規ホスト鍵を自動的に known_hosts へ追加する挙動)
https://man.openbsd.org/ssh_config

expect 利用時の制約とハマりポイント

expect は強力ですが、対話の自動化という性質上、いくつか押さえておきたい制約があります。実務でつまずきやすい点を整理します。

パターンマッチは既定で glob 方式

expect のパターンマッチは、既定でワイルドカード(glob)方式です。*?[ ] が特殊文字として扱われます。正規表現でマッチさせたい場合は -re、文字列の完全一致で判定したい場合は -ex を指定します。プロンプト文字列に記号が含まれる場合は、意図しないマッチを避けるため明示的なフラグ指定を検討します。

参考: Expect マニュアルページ(man7.org)
“new users may find it easier to start by reading the descriptions of spawn, send, expect”
(新規ユーザーは spawn、send、expect の説明から読むとよい)
https://man7.org/linux/man-pages/man1/expect.1.html

大文字小文字と改行コードの差異

password:Password: のように、プロンプトの表記は接続先によって異なります。-nocase を付与すると大文字小文字の差を吸収できます。また送信文字列の改行は、端末(pty)相手では \n よりも \r のほうが安定します。スクリプトが「プロンプトは出ているのに応答が送られない」という挙動を示す場合、改行コードが原因のことがあります。

タイムアウトと平文パスワードの課題

set timeout で指定した秒数を超えると、expect はタイムアウトします。上記の例では 10 秒に設定しており、ネットワーク遅延が大きい環境では延長を検討します。timeout ブロックでのエラー処理を用意しておくと、想定外のプロンプト変化で無限待機する事態を避けられます。

そして expect 最大の留意点は、スクリプト内にパスワードを平文で保持せざるを得ない点です。 expect は「対話の自動応答」を担うツールであり、認証情報の安全な管理そのものは解決しません。この課題への対処は、次のセクションの代替手段との使い分けで整理します。

SSH 自動ログインの代替手段と使い分け

expect は対話処理全般に使える汎用ツールですが、用途が SSH のパスワード自動化に限られる場合は、より適した手段があります。本番運用ではセキュリティ要件に応じて使い分けます。

パスワードを平文で保存するリスク

expect スクリプトで認証処理を自動化する場合、ファイル内にパスワードを平文(クリアテキスト)で記述するか、外部ファイルから読み込ませる必要があります。第三者にスクリプトを閲覧されると、システムへの不正アクセスにつながりかねません。最低限の対策として、スクリプトファイルのパーミッションを 600700 に設定し、所有者以外が読み取れないよう制限することをおすすめします。

SSH 公開鍵認証(推奨される標準手法)

サーバー間の SSH 接続や rsync によるファイル同期を自動化したい場合は、expect ではなく SSH 公開鍵認証(パスフレーズなしの鍵ペア)を利用するのが、インフラ運用の標準的な手法です。公開鍵認証なら、パスワードをスクリプトに保存せずに自動ログインを構成できます。詳細は関連記事『SSH 公開鍵認証の設定手順』を参照してください。

sshpass(SSH 限定の簡易手段)

SSH のパスワード認証だけを手早く自動化したい場合は、sshpass という選択肢もあります。Ubuntu では sudo apt install sshpass で導入でき、sshpass -p 'パスワード' ssh user@host の形で利用します。sshpass は専用の擬似端末で ssh を実行し、対話入力が来たかのように見せかける仕組みです。

ただし -p でコマンドラインにパスワードを渡すと、ps コマンドで同一ホストの他ユーザーにパスワードが見えてしまうため、ファイル渡し(-f)や環境変数渡し(-e)が比較的安全です。多くのディストリビューションが sshpass を既定で同梱しないのも、平文パスワードの取り扱いに対する安全側の判断です。

参考: sshpass マニュアルページ(Ubuntu)
“sshpass runs ssh in a dedicated tty, fooling it”
(sshpass は専用の tty で ssh を実行し、対話入力に見せかける)
https://manpages.ubuntu.com/manpages/jammy/man1/sshpass.1.html

比較表と選定基準

手段認証方式向くケース主なリスク・留意点
SSH 公開鍵認証鍵ペアサーバー間自動化の標準秘密鍵の管理・漏洩対策
expect対話の自動応答鍵非対応のレガシー機器、複数プロンプトの制御スクリプト内の平文パスワード
sshpassパスワードの非対話投入SSH 限定の簡易・一時的な自動化ps でのパスワード露出
Ansible 等鍵+Vault 等多数ホストの継続的な構成管理学習コスト、初期設計

選定の目安は次の通りです。

  • 自分が管理するサーバー間の自動化 → SSH 公開鍵認証
  • 鍵を登録できない古いルーターやスイッチ → expect
  • SSH 単発・簡易の自動化(信頼できる環境のみ)→ sshpass
  • 多数ホストを継続的に運用 → Ansible 等の構成管理ツール

まとめ

expect は、システムの出力を監視して定義済みの文字列を自動送信することで、SSH 接続などの対話処理をスクリプト化するツールです。導入と基本構文はシンプルですが、平文パスワードを扱う性質上、用途に応じた代替手段との使い分けが現実的です。要点を以下にまとめます。

  • expect はシステム出力を監視し定義済み文字列を自動送信する仕組み
  • Ubuntu では sudo apt install expect で導入可能
  • spawn・expect・send・interact の 4 コマンドによる対話制御
  • 送信文字列の改行は \r、パスワードは -nocase で取りこぼし防止
  • ホストキー確認は accept-new でプロンプト省略が可能
  • 平文パスワードのリスクから鍵認証や Vault との併用が現実的
  • サーバー間は鍵認証、レガシー機器は expect という使い分け

以上、最後までお読みいただきありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

関西を拠点に活動する、現役インフラエンジニア。経験20年超。

大手通信キャリアにて、中〜大規模インフラ(ネットワーク・サーバ・クラウド・セキュリティ)の設計・構築およびプロジェクトマネジメントに従事。現場で直面した技術課題への対処や、最新の脆弱性情報への実務対応を、一次情報として発信しています。

保有資格
CCIE Lifetime Emeritus(取得から20年以上)/ VCAP-DCA / Azure Solutions Architect Expert

▶ 運営者プロフィール(詳細)

目次