Linux expect コマンド:実践的な例と活用

LinuxBeginner
オンラインで実践に進む

はじめに

この実験(Lab)では、Linux の expect コマンドを使用して、対話型のコマンドラインアプリケーションを自動化する方法を学びます。expect コマンドは、SSH、FTP、その他の対話型プログラムなど、ユーザー入力を必要とするプログラムとスクリプトが対話することを可能にする強力な自動化ツールです。

この実験(Lab)の終わりには、以下のことができるようになります。

  • expect コマンドの目的と基本的な構文を理解する
  • SSH ログインを自動化するスクリプトを作成する
  • expect スクリプトで様々なプロンプトと応答を処理する

expect コマンドは、反復的なタスクに対する手動介入を大幅に削減し、システム管理やルーチンタスクを効率化します。まず、expect のインストールと基本的な構文の探求から始め、次に SSH ログインを自動化し、様々な対話型プロンプトを処理するためのスクリプトの作成に進みます。

Linux Commands Cheat Sheet

expect コマンドとその基本構文の理解

Linux の expect コマンドを使用すると、通常ユーザー入力を必要とする対話型のコマンドラインプログラムを自動化できます。これは、自動ログイン、ファイル転送、またはプログラムが入力プロンプトを表示するあらゆる状況に特に役立ちます。

expect のインストール

まず、expect パッケージがシステムにインストールされていることを確認しましょう。ターミナルを開き、以下を実行します。

which expect

expect が既にインストールされている場合、そのパス(/usr/bin/expect など)が表示されます。インストールされていない場合は、インストールする必要があります。

sudo apt-get update
sudo apt-get install -y expect

基本的な expect 構文の理解

expect コマンドは、Tcl (Tool Command Language) に基づくスクリプト言語を使用します。expect スクリプトの基本的な構造には、次のコマンドが含まれます。

  1. spawn: 対話するプロセスを開始します
  2. expect: スポーンされたプロセスからの特定の出力を待ちます
  3. send: スポーンされたプロセスに入力を送信します
  4. set timeout: 期待される出力の待ち時間を設定します

これらの概念を実証するために、簡単な expect スクリプトを作成しましょう。テキストエディタを開き、プロジェクトディレクトリに hello.exp という名前のファイルを作成します。

cd ~/project
nano hello.exp

ファイルに次の内容を入力します。

#!/usr/bin/expect -f

## タイムアウトを10秒に設定
set timeout 10

## bash プロセスをスポーン
spawn bash

## bash プロンプトを待機
expect "$ "

## bash プロセスにコマンドを送信
send "echo Hello from expect\r"

## もう一度 bash プロンプトを待機
expect "$ "

## bash セッションを終了
send "exit\r"

## プロセスが完了するのを待機
expect eof

Ctrl+OEnter の順に押してファイルを保存し、Ctrl+X で nano を終了します。

スクリプトを実行可能にします。

chmod +x ~/project/hello.exp

次に、スクリプトを実行します。

~/project/hello.exp

次のような出力が表示されるはずです。

spawn bash
$ echo Hello from expect
Hello from expect
$ exit
exit

スクリプトの各行の理解

スクリプトの各行が何をしているのか説明します。

  • #!/usr/bin/expect -f: これは、このスクリプトを実行するために expect インタープリターを使用するようにシステムに指示する shebang 行です。
  • set timeout 10: これは、それに続くすべての expect コマンドのタイムアウトを 10 秒に設定します。
  • spawn bash: これは、expect が対話する新しい bash シェルプロセスを開始します。
  • expect "$ ": これは、bash プロンプトが表示されるのを待ちます。
  • send "echo Hello from expect\r": これは、bash シェルにコマンドを送信します。末尾の \r に注意してください。これは Enter キーを押すことをシミュレートします。
  • expect "$ ": これは、コマンドが実行された後、もう一度 bash プロンプトを待ちます。
  • send "exit\r": これは、bash シェルを閉じる exit コマンドを送信します。
  • expect eof: これは、スポーンされたプロセスが終了するのを待ちます。

この簡単な例は、expect のコア機能を実証しています。次のステップでは、これらの概念を使用して、より実用的なスクリプトを作成します。

expect を使用したモック SSH ログインスクリプトの作成

このステップでは、SSH ログインプロセスをシミュレートする expect スクリプトを作成します。この環境では実際の SSH ログインを実行できないため、原則を示すモック スクリプトを作成します。

SSH 認証フローの理解

SSH を介してリモートサーバーに接続する場合、一般的な対話には以下が含まれます。

  1. ssh username@hostname で接続を開始する
  2. ホストキーを受け入れる(初めて接続する場合)
  3. プロンプトが表示されたらパスワードを入力する
  4. リモートシェルへのアクセスを取得する

expect がこのプロセスをどのように自動化できるかを示すために、モック SSH 環境を作成しましょう。

モック SSH サーバー スクリプトの作成

まず、パスワードを要求する SSH サーバーをシミュレートするスクリプトを作成しましょう。

cd ~/project
nano mock_ssh_server.sh

次の内容を入力します。

#!/bin/bash

echo "The authenticity of host 'mockserver' can't be established."
echo "RSA key fingerprint is SHA256:abcdefghijklmnopqrstuvwxyz123456."
echo "Are you sure you want to continue connecting (yes/no)? "
read answer

if [ "$answer" != "yes" ]; then
  echo "Host key verification failed."
  exit 1
fi

echo "Warning: Permanently added 'mockserver' (RSA) to the list of known hosts."
echo "Password: "
read -s password

if [ "$password" == "mockpassword" ]; then
  echo "Last login: Wed Nov 1 12:00:00 2023 from 192.168.1.100"
  echo "Welcome to Mock SSH Server"
  echo "mockuser@mockserver:~$ "

  while true; do
    read -p "" command
    if [ "$command" == "exit" ]; then
      echo "Connection to mockserver closed."
      exit 0
    else
      echo "Executing: $command"
      echo "mockuser@mockserver:~$ "
    fi
  done
else
  echo "Permission denied, please try again."
  exit 1
fi

ファイルを保存し、実行可能にします。

chmod +x ~/project/mock_ssh_server.sh

このスクリプトは以下をシミュレートします。

  • SSH ホスト検証プロンプト
  • パスワードプロンプト
  • コマンドに応答するシンプルなシェル

ログインを自動化する expect スクリプトの作成

次に、モック SSH サーバーとの対話を自動化する expect スクリプトを作成しましょう。

cd ~/project
nano ssh_login.exp

次の内容を入力します。

#!/usr/bin/expect -f

## 変数を設定
set timeout 10
set password "mockpassword"

## モック SSH サーバーを起動
spawn ./mock_ssh_server.sh

## ホスト検証プロンプトを処理
expect "Are you sure you want to continue connecting (yes/no)? "
send "yes\r"

## パスワードプロンプトを処理
expect "Password: "
send "$password\r"

## シェルプロンプトを待機
expect "mockuser@mockserver:~$ "

## コマンドを実行
send "ls -la\r"
expect "mockuser@mockserver:~$ "

## セッションを終了
send "exit\r"

## プロセスが完了するのを待機
expect eof

puts "\nSSH login automation completed successfully!"

ファイルを保存し、実行可能にします。

chmod +x ~/project/ssh_login.exp

自動ログインスクリプトの実行

次に、expect スクリプトを実行して、モック SSH サーバーとの対話を自動化しましょう。

cd ~/project
./ssh_login.exp

次のような出力が表示されるはずです。

spawn ./mock_ssh_server.sh
The authenticity of host 'mockserver' can't be established.
RSA key fingerprint is SHA256:abcdefghijklmnopqrstuvwxyz123456.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'mockserver' (RSA) to the list of known hosts.
Password:
Last login: Wed Nov 1 12:00:00 2023 from 192.168.1.100
Welcome to Mock SSH Server
mockuser@mockserver:~$ ls -la
Executing: ls -la
mockuser@mockserver:~$ exit
Connection to mockserver closed.

SSH login automation completed successfully!

スクリプトの説明

expect スクリプトの各部分が何をしているのか説明します。

  1. set timeout 10: すべての expect コマンドのグローバルタイムアウトを 10 秒に設定します。
  2. set password "mockpassword": パスワードを変数に格納します。
  3. spawn ./mock_ssh_server.sh: モック SSH サーバー スクリプトを開始します。
  4. expect "Are you sure you want to continue connecting (yes/no)? ": ホスト検証プロンプトを待ちます。
  5. send "yes\r": ホストキーを受け入れるために "yes" を送信します。
  6. expect "Password: ": パスワードプロンプトを待ちます。
  7. send "$password\r": パスワードを送信します。
  8. expect "mockuser@mockserver:~$ ": シェルプロンプトを待ちます。
  9. send "ls -la\r": ファイルを一覧表示するコマンドを送信します。
  10. expect "mockuser@mockserver:~$ ": もう一度シェルプロンプトを待ちます。
  11. send "exit\r": セッションを閉じる exit コマンドを送信します。
  12. expect eof: プロセスが終了するのを待ちます。
  13. puts "\nSSH login automation completed successfully!": 成功メッセージを出力します。

この例は、ホストキーの受け入れから、リモートサーバーでのコマンドの実行、安全な終了まで、expect を使用して SSH ログインプロセス全体を自動化する方法を示しています。

expect を使用した複数のプロンプトと応答の処理

現実世界のシナリオでは、対話型プログラムは多くの場合、複数のプロンプトを表示し、さまざまな条件に基づいて異なる応答を必要とする場合があります。このステップでは、expect スクリプトで複数のプロンプトと条件付き応答を処理する方法を学びます。

条件付き expect ブロックの理解

expect コマンドは、さまざまな可能性のあるプロンプトを処理するために、パターンアクションブロック構造で使用できます。

expect {
    "pattern1" { actions for pattern1 }
    "pattern2" { actions for pattern2 }
    timeout { actions for timeout }
    eof { actions for end of file }
}

この構造により、スクリプトは受信する出力に応じて異なる方法で応答できます。

マルチプロンプト処理スクリプトの作成

複数のプロンプトを持つプログラムをシミュレートするスクリプトを作成しましょう。

cd ~/project
nano multi_prompt.sh

次の内容を入力します。

#!/bin/bash

echo "Please select an option:"
echo "1. Show date and time"
echo "2. List files"
echo "3. Show system info"
echo "4. Exit"
echo -n "Enter your choice (1-4): "
read choice

case $choice in
  1)
    echo "Current date and time:"
    date
    ;;
  2)
    echo "File listing:"
    ls -la
    ;;
  3)
    echo "System information:"
    uname -a
    ;;
  4)
    echo "Exiting program..."
    exit 0
    ;;
  *)
    echo "Invalid option. Please enter a number between 1 and 4."
    exit 1
    ;;
esac

echo "Do you want to continue? (yes/no): "
read answer

if [ "$answer" == "yes" ]; then
  echo "Continuing..."
  echo "Operation completed successfully."
else
  echo "Exiting program..."
fi

ファイルを保存し、実行可能にします。

chmod +x ~/project/multi_prompt.sh

次に、このプログラムと対話し、すべての可能なプロンプトを処理する expect スクリプトを作成しましょう。

cd ~/project
nano handle_prompts.exp

次の内容を入力します。

#!/usr/bin/expect -f

## タイムアウトを設定
set timeout 10

## マルチプロンプトプログラムを開始
spawn ./multi_prompt.sh

## 選択プロンプトを待機
expect "Enter your choice (1-4): "

## ランダムな選択を生成 (1-3)
set choice [expr {int(rand() * 3) + 1}]
send "$choice\r"

## 選択に基づいて結果を処理
switch $choice {
    1 {
        expect "Current date and time:"
        expect "Do you want to continue? (yes/no): "
    }
    2 {
        expect "File listing:"
        expect "Do you want to continue? (yes/no): "
    }
    3 {
        expect "System information:"
        expect "Do you want to continue? (yes/no): "
    }
}

## 続行プロンプトを処理
expect {
    "Do you want to continue? (yes/no): " {
        ## 70% の確率で yes、30% の確率で no
        if {rand() < 0.7} {
            send "yes\r"
            expect "Operation completed successfully."
        } else {
            send "no\r"
            expect "Exiting program..."
        }
    }
    timeout {
        puts "Timeout waiting for continue prompt"
        exit 1
    }
}

## プログラムが完了するのを待機
expect eof

puts "\nMulti-prompt handling completed successfully!"

ファイルを保存し、実行可能にします。

chmod +x ~/project/handle_prompts.exp

マルチプロンプト処理スクリプトの実行

次に、マルチプロンプトプログラムと対話する expect スクリプトを実行しましょう。

cd ~/project
./handle_prompts.exp

このスクリプトを実行するたびに、オプションの 1 つがランダムに選択され、続行するか終了するかがランダムに決定されます。以下は出力例です。

spawn ./multi_prompt.sh
Please select an option:
1. Show date and time
2. List files
3. Show system info
4. Exit
Enter your choice (1-4): 2
File listing:
total 20
drwxr-xr-x 2 labex labex 4096 Nov  1 10:00 .
drwxr-xr-x 4 labex labex 4096 Nov  1 10:00 ..
-rwxr-xr-x 1 labex labex  345 Nov  1 10:00 handle_prompts.exp
-rwxr-xr-x 1 labex labex  578 Nov  1 10:00 multi_prompt.sh
-rwxr-xr-x 1 labex labex  221 Nov  1 10:00 ssh_login.exp
Do you want to continue? (yes/no): yes
Continuing...
Operation completed successfully.

Multi-prompt handling completed successfully!

より高度な expect スクリプトの作成

次に、予期しないプロンプトとエラーを処理できる、より高度なスクリプトを作成しましょう。

cd ~/project
nano advanced_expect.exp

次の内容を入力します。

#!/usr/bin/expect -f

## タイムアウトを設定
set timeout 10

## 変数を定義
set program "./multi_prompt.sh"
set max_retries 3
set retry_count 0

## エラーを処理するプロシージャを定義
proc handle_error {} {
    global retry_count max_retries program
    incr retry_count

    if {$retry_count < $max_retries} {
        puts "\nRetrying... Attempt $retry_count of $max_retries"
        ## プログラムを再開
        spawn $program
        return 1
    } else {
        puts "\nMaximum retry attempts reached. Exiting."
        exit 1
    }
}

## プログラムを開始
spawn $program

## メインインタラクションループ
while {$retry_count < $max_retries} {
    expect {
        "Enter your choice (1-4): " {
            send "1\r"  ## 決定論的な動作のために常にオプション 1 を選択
        }
        "Invalid option" {
            puts "\nReceived invalid option message."
            if {[handle_error]} continue
        }
        "Current date and time:" {
            ## 日付出力を正常に取得
        }
        "Do you want to continue? (yes/no): " {
            send "yes\r"
        }
        "Operation completed successfully." {
            puts "\nAdvanced expect script completed successfully!"
            break
        }
        timeout {
            puts "\nTimeout occurred waiting for prompt."
            if {[handle_error]} continue
        }
        eof {
            puts "\nUnexpected end of file."
            if {[handle_error]} continue
        }
    }
}

## プログラムが完了するのを待機
expect eof

ファイルを保存し、実行可能にします。

chmod +x ~/project/advanced_expect.exp

高度なスクリプトを実行します。

cd ~/project
./advanced_expect.exp

出力例:

spawn ./multi_prompt.sh
Please select an option:
1. Show date and time
2. List files
3. Show system info
4. Exit
Enter your choice (1-4): 1
Current date and time:
Wed Nov  1 10:00:00 UTC 2023
Do you want to continue? (yes/no): yes
Continuing...
Operation completed successfully.

Advanced expect script completed successfully!

高度なスクリプトの理解

この高度なスクリプトは、いくつかの重要な expect テクニックを示しています。

  1. エラー処理: エラーまたは予期しない応答を処理するために、再試行メカニズムを使用します。
  2. プロシージャ: 再利用可能なエラー処理のために、handle_error というカスタムプロシージャを定義します。
  3. 制御フロー: 成功または最大再試行回数に達するまで、対話を維持するために while ループを使用します。
  4. 複数の expect パターン: 複数の異なるパターンを処理し、それぞれに対して適切なアクションを実行します。
  5. パターンの順序: expect ブロック内のパターンの順序は重要です。より具体的なパターンは、より一般的なパターンの前に配置する必要があります。

これらのテクニックは、フローが異なる場合やエラーが発生する可能性がある複雑な対話型プログラムを自動化するために適用できます。

一般的なタスクに対する実用的な expect スクリプトの作成

このステップでは、システム管理者がよく自動化する必要がある一般的なタスクに対して、実用的な expect スクリプトを作成します。ファイル操作、ユーザーインタラクション、およびシステム監視に焦点を当てます。

expect を使用したファイル転送の自動化

scp コマンドを使用してファイルの転送を自動化する expect スクリプトを作成しましょう。この環境では実際のファイル転送を実行できないため、シミュレートします。

cd ~/project
nano file_transfer.sh

SCP のようなファイル転送をシミュレートするために、次の内容を入力します。

#!/bin/bash

echo "scp file transfer simulation"
echo "Source file: $1"
echo "Destination: $2"
echo "Password: "
read -s password

if [ "$password" == "transfer123" ]; then
  echo "Transferring file..."
  echo "0%"
  sleep 1
  echo "25%"
  sleep 1
  echo "50%"
  sleep 1
  echo "75%"
  sleep 1
  echo "100%"
  echo "File transfer completed successfully."
else
  echo "Authentication failed."
  exit 1
fi

ファイルを保存し、実行可能にします。

chmod +x ~/project/file_transfer.sh

次に、このファイル転送を自動化する expect スクリプトを作成しましょう。

cd ~/project
nano file_transfer.exp

次の内容を入力します。

#!/usr/bin/expect -f

## 変数を設定
set timeout 10
set source_file "local_file.txt"
set destination "user@remote:/path/to/destination/"
set password "transfer123"

## ダミーのソースファイルを作成
spawn bash -c "echo 'This is a test file' > $source_file"
expect eof

## ファイル転送シミュレーションを開始
spawn ./file_transfer.sh $source_file $destination

## パスワードプロンプトを処理
expect "Password: "
send "$password\r"

## 転送の進捗を監視
expect "0%"
puts "Transfer started..."

expect "25%"
puts "Transfer 1/4 complete..."

expect "50%"
puts "Transfer 1/2 complete..."

expect "75%"
puts "Transfer 3/4 complete..."

expect "100%"
puts "Transfer almost done..."

expect "File transfer completed successfully."
puts "File transfer automation completed!"

## ダミーファイルをクリーンアップ
spawn bash -c "rm $source_file"
expect eof

ファイルを保存し、実行可能にします。

chmod +x ~/project/file_transfer.exp

ファイル転送自動化スクリプトを実行します。

cd ~/project
./file_transfer.exp

出力例:

spawn bash -c echo 'This is a test file' > local_file.txt
spawn ./file_transfer.sh local_file.txt user@remote:/path/to/destination/
scp file transfer simulation
Source file: local_file.txt
Destination: user@remote:/path/to/destination/
Password:
Transferring file...
0%
Transfer started...
25%
Transfer 1/4 complete...
50%
Transfer 1/2 complete...
75%
Transfer 3/4 complete...
100%
Transfer almost done...
File transfer completed successfully.
File transfer automation completed!
spawn bash -c rm local_file.txt

expect を使用したユーザー作成の自動化

次に、ユーザー作成を自動化する expect スクリプトを作成しましょう。ここでも、このプロセスをシミュレートします。

cd ~/project
nano create_user.sh

次の内容を入力します。

#!/bin/bash

echo "User creation utility"
echo "Please enter new username: "
read username

echo "Please enter password for $username: "
read -s password

echo "Please confirm password: "
read -s password_confirm

if [ "$password" != "$password_confirm" ]; then
  echo "Error: Passwords do not match."
  exit 1
fi

echo "Creating user $username..."
echo "User $username created successfully."
echo "Do you want to add this user to the sudo group? (yes/no): "
read sudo_choice

if [ "$sudo_choice" == "yes" ]; then
  echo "Adding $username to sudo group..."
  echo "User $username added to sudo group."
fi

echo "User setup completed."

ファイルを保存し、実行可能にします。

chmod +x ~/project/create_user.sh

次に、ユーザー作成を自動化する expect スクリプトを作成しましょう。

cd ~/project
nano create_user.exp

次の内容を入力します。

#!/usr/bin/expect -f

## 変数を設定
set timeout 10
set username "testuser"
set password "P@ssw0rd123"
set add_sudo "yes"

## ユーザー作成ユーティリティを開始
spawn ./create_user.sh

## ユーザー名プロンプトを処理
expect "Please enter new username: "
send "$username\r"

## パスワードプロンプトを処理
expect "Please enter password for $username: "
send "$password\r"

## パスワード確認プロンプトを処理
expect "Please confirm password: "
send "$password\r"

## ユーザー作成の確認を待機
expect "User $username created successfully."

## sudo プロンプトを処理
expect "Do you want to add this user to the sudo group? (yes/no): "
send "$add_sudo\r"

## sudo に追加することを選択した場合、確認を待機
if {$add_sudo == "yes"} {
    expect "User $username added to sudo group."
}

## 完了を待機
expect "User setup completed."

puts "\nUser creation automation completed successfully!"

ファイルを保存し、実行可能にします。

chmod +x ~/project/create_user.exp

ユーザー作成自動化スクリプトを実行します。

cd ~/project
./create_user.exp

出力例:

spawn ./create_user.sh
User creation utility
Please enter new username:
testuser
Please enter password for testuser:
Please confirm password:
Creating user testuser...
User testuser created successfully.
Do you want to add this user to the sudo group? (yes/no):
yes
Adding testuser to sudo group...
User testuser added to sudo group.
User setup completed.

User creation automation completed successfully!

実用的な expect スクリプトの理解

作成した実用的なスクリプトは、実際の自動化に関するいくつかの重要な概念を示しています。

  1. シーケンシャルインタラクション: 両方のスクリプトは、定義された一連のプロンプトと応答に従います。
  2. 進捗監視: ファイル転送スクリプトは進捗を監視し、ユーザーフレンドリーな更新を提供します。
  3. 条件付きロジック: ユーザー作成スクリプトは、sudo オプションを処理するために条件付きロジックを使用します。
  4. 環境のセットアップとクリーンアップ: ファイル転送スクリプトは、テストファイルを作成し、クリーンアップします。

これらのテクニックは、次のような多くの一般的なシステム管理タスクを自動化するために適用できます。

  • リモートバックアップ
  • ソフトウェアのインストール
  • システム構成
  • バッチ操作

expect を習得することで、手動での介入が必要になる可能性のある複雑な対話型プロセスを自動化し、時間を節約し、人的ミスの可能性を減らすことができます。

まとめ

この実験では、Linux で expect コマンドを使用して、対話型のコマンドラインアプリケーションを自動化する方法を学びました。以下の点で実用的な経験を積みました。

  • expect コマンドの基本的な構文のインストールと理解
  • SSH ログインを自動化し、さまざまな認証プロンプトを処理するスクリプトの作成
  • expect スクリプトでの複数のプロンプトと応答の処理
  • 一般的なシステム管理タスクのための実用的な自動化スクリプトの作成

expect コマンドは、対話型プロセスを自動化する必要があるシステム管理者や開発者にとって強力なツールです。expect を使用することで、繰り返し発生するタスクへの手動介入の必要性をなくし、時間を節約し、人的ミスのリスクを減らすことができます。

この実験からの主なポイント:

  • expect コマンドは、プログラムと対話するためにパターンアクションモデルを使用します。
  • さまざまな可能性のあるプロンプトとエラー状態を処理することにより、スクリプトをより堅牢にすることができます。
  • 条件付きロジックとカスタムプロシージャを使用して、複雑なインタラクションを自動化できます。
  • 実用的な自動化は、一般的なシステムタスクの効率を大幅に向上させることができます。

この実験で学んだスキルにより、さまざまな対話型コマンドラインアプリケーション用の独自の自動化スクリプトを作成できるようになりました。

Linux Commands Cheat Sheet