• このエントリーをはてなブックマークに追加

【linux豆知識】自動化といえばコレ!expectの使い方・変数の扱い

  • このエントリーをはてなブックマークに追加

expect
zaco muraです。

以前、【linux豆知識】readコマンドを使って対話的なシェルスクリプトを作るという記事を書きましたが、対話的なシェルスクリプトといえば expect を使うことが多いです。

このexpectはとても便利なコマンドですが、なかなかクセがあって使いこなすのが難しいです。今回はハマりがちな変数周りのところを中心に説明したいと思います。

スポンサーリンク
Sponsords Link

expectとは

expectは対話型の処理を自動化するためのツールです。
具体例で言うと、例えば

1.AサーバからBサーバにSSHログインし、
2.Bサーバ上で処理を実行する

ということを自動的に行えるようになります。

インストール方法

CentOS系であればyumでインストール可能です。(以下はCentOS7の例)

# yum install expect
読み込んだプラグイン:fastestmirror
base                                                                                                                           | 3.6 kB  00:00:00
epel/x86_64/metalink                                                                                                           | 5.5 kB  00:00:00
epel                                                                                                                           | 4.3 kB  00:00:00
extras                                                                                                                         | 3.4 kB  00:00:00
openstack-liberty                                                                                                              | 2.9 kB  00:00:00
updates                                                                                                                        | 3.4 kB  00:00:00
(1/4): epel/x86_64/updateinfo                                                                                                  | 503 kB  00:00:00
(2/4): extras/7/x86_64/primary_db                                                                                              | 101 kB  00:00:00
(3/4): updates/7/x86_64/primary_db                                                                                             | 3.1 MB  00:00:00
(4/4): epel/x86_64/primary_db                                                                                                  | 3.9 MB  00:00:01
Determining fastest mirrors
 * base: ftp.iij.ad.jp
 * epel: epel.mirror.net.in
 * extras: ftp.iij.ad.jp
 * updates: ftp.iij.ad.jp
依存性の解決をしています
--> トランザクションの確認を実行しています。
---> パッケージ expect.x86_64 0:5.45-14.el7_1 を インストール
--> 依存性の処理をしています: libtcl8.5.so()(64bit) のパッケージ: expect-5.45-14.el7_1.x86_64
--> トランザクションの確認を実行しています。
---> パッケージ tcl.x86_64 1:8.5.13-8.el7 を インストール
--> 依存性解決を終了しました。

依存性を解決しました

======================================================================================================================================================
 Package                           アーキテクチャー                  バージョン                                 リポジトリー                     容量
======================================================================================================================================================
インストール中:
 expect                            x86_64                            5.45-14.el7_1                              base                            262 k
依存性関連でのインストールをします:
 tcl                               x86_64                            1:8.5.13-8.el7                             base                            1.9 M

トランザクションの要約
======================================================================================================================================================
インストール  1 パッケージ (+1 個の依存関係のパッケージ)

総ダウンロード容量: 2.1 M
インストール容量: 4.9 M
Is this ok [y/d/N]: y
Downloading packages:
(1/2): expect-5.45-14.el7_1.x86_64.rpm                                                                                         | 262 kB  00:00:00
(2/2): tcl-8.5.13-8.el7.x86_64.rpm                                                                                             | 1.9 MB  00:00:00
------------------------------------------------------------------------------------------------------------------------------------------------------
合計                                                                                                                  2.7 MB/s | 2.1 MB  00:00:00
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  インストール中          : 1:tcl-8.5.13-8.el7.x86_64                                                                                             1/2
  インストール中          : expect-5.45-14.el7_1.x86_64                                                                                           2/2
  検証中                  : 1:tcl-8.5.13-8.el7.x86_64                                                                                             1/2
  検証中                  : expect-5.45-14.el7_1.x86_64                                                                                           2/2

インストール:
  expect.x86_64 0:5.45-14.el7_1

依存性関連をインストールしました:
  tcl.x86_64 1:8.5.13-8.el7

完了しました!

基本の使い方

まず、先程例に挙げた使い方をしてみます。
以下のようなシェルスクリプトを書いてみましょう。

#!/bin/sh
expect -c "
        spawn ssh zacomura@192.168.0.1
        expect \"s password:\"
        send \"password\n\"
        expect \"~]\"
        send \"echo ssh_test\n\"
        expect \"~]\"
        send \"exit\n\"
        expect \"~]\"
"

パッと見てもなんのこっちゃわからないですが、まずは動かしてみましょう

[root@server1 ~]# sh expect_test.sh
spawn ssh zacomura@192.168.0.1
zacomura@192.168.0.1's password:
Last login: Sat Mar  5 10:20:43 2016 from 192.168.0.2
[zacomura@192.168.0.1 ~]# echo ssh_test
ssh_test
[zacomura@192.168.0.1 ~]# exit
ログアウト
Connection to 192.168.0.1 closed.

動きました。
このスクリプトは先程書いたように、

1.Aサーバ(192.168.0.2)からBサーバ(192.168.0.1)にSSHログインし、
2.Bサーバ上で処理(echo “ssh_test”)を実行する

という動作をします。

基本動作の解説

expectは入力それに対する応答を順番に書いていくことで対話的な処理を実現します。
ですので、スクリプトは基本的に以下のような構成になります。

1 : spawn “対話的に実行したいコマンド”
2 : expect “1のコマンドを実行した後に返ってくる応答の文字列”
3 : send “次に入力したいコマンドや文字列”
以降、2と3を繰り返す

先程のスクリプトを当てはめると

spawn ssh zacomura@192.168.0.1

これが対話的に実行したいコマンドです。
(“spawn”というのはとりあえずおまじないとして覚えてください)

次に、

        expect \"s password:\"
        send \"password\n\"

1行目の“s password:” が期待する応答、つまりsshログインする時のパスワードプロンプトです。
2行目の“send \”password\n\” が入力、つまりzacomura@192.168.0.1にログインするパスワードです。

なお、expectはコマンドの中身自体を””で囲っていますので、さらに””を使う場合にはバックスラッシュ(\)でクオートする必要があります。(ここがexpectのややこしいところです)

また、コマンドを入力するときには必ずEnterキーを押すかと思いますが、末尾に”\n”を入れることで代替できます。忘れないように入れましょう。

その次の

        expect \"~]\"
        send \"echo ssh_test\n\"

ここも先程の部分と同じです。
ログインに成功すると、“[zacomura@192.168.0.1 ~]$”というプロンプトが返ってきます。今回はこの中の “~]” を待ったうえで echo “ssh_test” というコマンドを実行しています。

ちなみに、$を入れると変数と解釈されてしまいちょっと面倒なので “~]” までにしています。

以降もこの繰り返しになります。

expectの変数の扱い方

先程も少し触れましたが、expectでは変数やクオートの扱い方がとてもややこしいです。
自分の経験では特に

・シェルスクリプト内の変数を使う
・SSH先の端末の変数を使う

と言ったところが面倒です。こちらについて説明していきます。

expectでシェルスクリプト内の変数を使う

まず、expectを実行しているシェルスクリプトで定義されている変数を使う場合です。

#!/bin/sh
HENSU="aaaaaa"
expect -c "
    spawn ssh ~~~~
    ## この先で ${HENSU} を使いたい
"

これを実行するためには、変数の前後を””で囲う必要があります。

#!/bin/sh
HENSU=aaaaaa
expect -c "
        spawn ssh zacomura@192.168.0.1
        expect \"s password:\"
        send \"password\n\"
        expect \"~]\"
        send \"echo "${HENSU}"\n\"  ### <==ここ
        expect \"~]\"
        send \"exit\n\"
        expect \"~]\"
"

これにより、一時的にexpectの外での実行となるためシェルスクリプト内の変数として処理されます。

expectでSSH先の端末の変数を使う

次はSSHでログインしている先の端末で定義されている変数を使う場合です。
つまり、こういうことです。

#!/bin/sh
HENSU="aaaaaa"
expect -c "
    spawn ssh ~~~~
    expect ~~~~
    send \"HENSU=bbbbb\" ###ここで変数を定義
    expect ~~~~
    ## この先で ${HENSU} を使いたい
"

これは、変数の前にバックスラッシュ(\)を3つ付けることで実現できます。
コードとしては以下のようになります。

#!/bin/sh
expect -c "
        spawn ssh zacomura@192.168.0.1
        expect \"s password:\"
        send \"password\n\"
        expect \"~]\"
        send \"HENSU=aaaaa \n\" ### SSH先の端末で変数$HENSUを定義
        expect \"~]\"
        send \"echo \\\$HENSU \n\"  ### <==ここ
        expect \"~]\"
        send \"exit\n\"
        expect \"~]\"
"

これで複雑な処理もうまくできるようになり、応用範囲が大きく広がると思います。

スポンサーリンク
Sponsords Link
  • このエントリーをはてなブックマークに追加

ZacoDesign

スポンサーリンク
Sponsords Link