予測を使用して正規表現を使用して「$」で終わる項目を一致させるにはどうすればよいですか?

予測を使用して正規表現を使用して「$」で終わる項目を一致させるにはどうすればよいですか?

を使用してリモートサーバーにいくつかのコマンドを実行したいと思いますexpect。ただし、使用時に.*\$$コマンドを送信するようにリモートプロンプトを常に一致させることはできません。

以下は私のスクリプトです。

#!/bin/ksh
while read -r line; do
/usr/linux/bin/expect -c "
set timeout 20
spawn ssh padmin@$line
expect {
    -re \".*(P|p)assword:\" {send \"$2\r\"}
}
expect {
    -re \".*\$$\" {send \"ls -lt cfgbackups|grep gz|head -n 1|awk '{print \$9}'\r\"}
}
expect {
    -re \".*\$$\" {exit 0}
    eof {exit 0}
}
send_user \"$expect_out(1,string)\n\"
" 2>"$line".errlog &
done <"$1"

デバッグモードを使用して確認すると、次のメッセージが表示されます。

expect version 5.45.4
spawn ssh [email protected]
parent: waiting for sync byte
parent: telling child to go ahead
parent: now unsynchronized from child
spawn: returns {3474502}
Gate keeper glob pattern for '(P|p)assword:' is ''. Not usable, disabling the performance booster.

expect: does "" (spawn_id exp4) match regular expression "(P|p)assword:"? (No Gate, RE only) gate=yes re=no
[email protected]'s password: 
expect: does "[email protected]'s password: " (spawn_id exp4) match regular expression "(P|p)assword:"? (No Gate, RE only) gate=yes re=yes
expect: set expect_out(0,string) "password:"
expect: set expect_out(1,string) "p"
expect: set expect_out(spawn_id) "exp4"
expect: set expect_out(buffer) "[email protected]'s password:"
send: sending "padmin\r" to { exp4 }
Gate keeper glob pattern for '^:.*$' is ':*'. Activating booster.

expect: does " " (spawn_id exp4) match regular expression "^:.*$"? Gate ":*"? gate=no
"send_user"? no


expect: does " \r\n" (spawn_id exp4) match regular expression "^:.*$"? Gate ":*"? gate=no
"send_user"? no
Last unsuccessful login: Tue Jul  7 03:04:50 BEIST 2020 on ssh from 192.168.0.5
Last login: Tue Jul  7 15:18:11 BEIST 2020 on ssh from 192.168.0.4
Welcome!
/etc/profile[63]: hostname:  not found.

expect: does " \r\nLast unsuccessful login: Tue Jul  7 03:04:50 BEIST 2020 on ssh from 192.168.0.5\r\nLast login: Tue Jul  7 15:18:11 BEIST 2020 on ssh from 192.168.0.4\r\nWelcome!\r\n/etc/profile[63]: hostname:  not found.\r\n" (spawn_id exp4) match regular expression "^:.*$"? Gate ":*"? gate=yes re=no
"send_user"? no
:/home/padmin$ 
expect: does " \r\nLast unsuccessful login: Tue Jul  7 03:04:50 BEIST 2020 on ssh from 192.168.0.5\r\nLast login: Tue Jul  7 15:18:11 BEIST 2020 on ssh from 192.168.0.4\r\nWelcome!\r\n/etc/profile[63]: hostname:  not found.\r\n:/home/padmin$ " (spawn_id exp4) match regular expression "^:.*$"? Gate ":*"? gate=yes re=no
"send_user"? no
expect: timed out
Gate keeper glob pattern for '^:.*$' is ':*'. Activating booster.

expect: does " \r\nLast unsuccessful login: Tue Jul  7 03:04:50 BEIST 2020 on ssh from 192.168.0.5\r\nLast login: Tue Jul  7 15:18:11 BEIST 2020 on ssh from 192.168.0.4\r\nWelcome!\r\n/etc/profile[63]: hostname:  not found.\r\n:/home/padmin$ " (spawn_id exp4) match regular expression "^:.*$"? Gate ":*"? gate=yes re=no
sighandler: handling signal(2)
async event handler: Tcl_Eval(exit 130)

それでは、この問題をどのように解決するのですか?ありがとうございます。

答え1

/usr/linux/bin/expect -c ...;に渡された文字列を見てみましょう。コマンドは実際に何を受け取りますか?コマンドを次に置き換えると、echo予想される出力を表示できます。

expect {
    -re ".*$" {send "ls -lt cfgbackups|grep gz|head -n 1|awk '{print $9}'\r"}
}

それでは、渡された文字列を使って同じことをしましょうsend。今回は文字列を解析するために使用する必要があるため、これをExpectコマンドにexpect置き換えます。たとえば、次のようになります。sendsend_user

expect <<\!
send_user "ls -lt cfgbackups|grep gz|head -n 1|awk '{print $9}'\r"
!

予想されるエラーが発生します。

can't read "9": no such variable

$9したがって、を使用して補間を停止する必要があります\$9

元のkshスクリプトに戻り、\二重引用符で囲まれた文字列内に一重引用符を生成するには、を使用する必要があります\\。したがって、最終的に解決策は次のとおりです。

expect {
    -re \".*$\" {send \"ls -lt cfgbackups|grep gz|head -n 1|awk '{print \\\$9}'\r\"}
}

一重引用符と二重引用符の間にスペースがない場合は、交互に使用してこれらの問題を減らすことができます。例:echo "hi"'there'生産hithereecho '"who'"'s"'"'生産"who's"。ここで、ドキュメントの使用とスタイルは<<!別のレベルの引用を提供します。<<\!

答え2

予想されるコードをシェルスクリプトに混在させるときは、heredocを使用するのが最善です。これにより、少なくとも1段階の引用地獄を避けることができます。 Tcl / expectでは、正規表現は引用符ではなく中括弧で囲むのが最善です。これにより、バックスラッシュをより簡単に処理できます。

/usr/linux/bin/expect <<END_EXPECT 2>"$line".errlog &
    set timeout 20
    spawn ssh padmin@$line
    expect {
        -re {.*(P|p)assword:} {send "$2\r"}
    }
    expect {
        -re {.*\$$} {send "ls -lt cfgbackups|grep gz|head -n 1|awk '{print \$9}'\r"}
    }
    expect {
        -re {.*\$$} {exit 0}
        eof {exit 0}
    }
    send_user "$expect_out(1,string)\n"
END_EXPECT

改行とキャリッジリターンを書く必要があるかどうかわかりません\r\\r試してみてください。

環境を介して予想されるシェル変数を渡すことができ、heredoc全体を引用することができます。

host="$line" passwd="$2" /usr/linux/bin/expect <<'END_EXPECT' 2>"$line".errlog &
    # ...........................................^..........^
    set timeout 20
    spawn ssh padmin@$env(host)
    expect {
        -re {.*(P|p)assword:} {send "$env(passwd)\r"}
    }
    expect {
        -re {.*\$$} {send "ls -lt cfgbackups|grep gz|head -n 1|awk '{print \$9}'\r"}
    }
    expect {
        -re {.*\$$} {exit 0}
        eof {exit 0}
    }
    send_user "$expect_out(1,string)\n"
END_EXPECT

この行の目的は何ですかsend_user?予測モードでは、何もキャプチャせず、実際にexpect最後のコマンドで予測を終了します。そこで何を見ることができると思いますか?

答え3

問題を発見しました。問題は$シンボルです。\\\$期待される出力を使用すると、スクリプトは正常に動作します。$正規表現でも特殊記号だからです。正規表現を使用する場合は、正規表現の\$末尾記号で表されます。だから我々は\\\$それを使用して変換する必要があります\$。ありがとうございます。

関連情報