Bashスクリプトで区切り文字をエスケープする方法

Bashスクリプトで区切り文字をエスケープする方法

入力ファイルを読み込み、heredocを使用してサーバーにsshで接続するbashスクリプトがありますが、入力ファイルからいくつかの特殊文字をエスケープするのに問題があります。これが私が持っているものです...


以下を含む input.txt というファイルがあります。

1.2.3.4:abcdefg
2.3.4.5:abc$def

以下のようにbashスクリプトがあります。パスワードに「$」文字が含まれていない場合はうまく機能しますが、パスワードに「$」が含まれている場合は$の後ろの部分を変数として扱うため、操作は中断されます。

#!/bin/bash

if [ -e "input.txt" ]; then
    while read i; do

/usr/bin/expect <(cat << EOD
set timeout 15
spawn ssh "user@$(echo $i | cut -d: -f 1)"
#######################

expect "yes/no" {
    send "yes\r"
    expect "Password:" { send "$(echo $i | cut -d: -f 2)\r" }
} "Password:" { send "$(echo $i | cut -d: -f 2)\r" }
expect -re "day: $" { send "\r" }
expect ":" { send "\r" }
expect -re "# $" { send "date\r" }
expect -re "# $" { send "exit\r" }
EOD
)
    done < input.txt
fi

これを実行して2番目のIPセットに達すると、次のエラーが発生します。

spawn ssh [email protected]

Unauthorized use of this system is prohibited and may be prosecuted
to the fullest extent of the law. By using this system, you implicitly
agree to monitoring by system management and law enforcement authorities.
If you do not agree with these terms, DISCONNECT NOW.

Password: can't read "def": no such variable
    while executing
"send "abc$def\r" "
    invoked from within
"expect "yes/no" {
    send "yes\r"
    expect "Password:" { send "abc$def\r" }
} "Password:" { send "abc$def\r" }"
    (file "/dev/fd/63" line 5)

誰でもどんなアイデアがありますか?私は二重引用符の中に一重引用符と私が考えることができるすべてを試しましたが、まだ動作しませんでした。事前にありがとう

答え1

ここでドキュメントの変数を拡張したくない場合は、EODを引用してください。

cat << 'EOD'
...
EOD

たとえば、次のファイルは次のようになります。

cat <<EOF
test\$literal
var$(echo this)
EOF

次の結果が生成されます。

test$literal
varthis

答え2

問題は、ここのシェルまたはドキュメントにあるのではなく、変数を$def変数として参照して拡張したいExpectにあります。

ここでbar変数はExpectに設定されて拡張されます(これは一重引用符であるため、シェルはそれを拡張しません$bar)。

$ expect -f - <<< 'set bar abcdefg; send_user "foo$bar\n"'
fooabcdefg

これはあなたが見たのと同じエラーを引き起こします(予測スクリプトは二重引用符で囲まれているので、$var する以前とは異なり、シェルから拡張します。):

$ var='foo$bar'; expect -f - <<< "send_user \"$var\n\""
can't read "bar": no such variable
    while executing
"send_user 'foo$bar\n'"

脱出する必要が$ありますが、少なくとも脱出する必要があります。[なぜならこれは Expect やむしろ Expect の基盤となる Tcl でも特別だからです。おそらく他の人もいるようですが、よくわかりません...

コマンドライン引数を介して値を渡す方が簡単です。

$ var='foo$bar[quux]'
$ expect -f - "$var" <<< 'set var [lindex $argv 0]; send_user "$var\n"'
foo$bar[quux]

または環境を通して:

$ export var
$ expect -f - "$var" <<< 'set var $::env(var); send_user "$var\n"'
foo$bar[quux]

コードの機能に近いです。

#!/bin/bash
line='2.3.4.5:abc$def'
IFS=: read host pw <<< "$line"
export host pw

expect -f - << 'EOF'
set host $::env(host)
set pw $::env(pw)
send_user "host is '$host' pw is '$pw'\n"
EOF

環境に渡された変数を使用すると、シェルがsを拡張せずに必要に応じて$Expect変数を使用できるようにhere-docを引用できます$host$pw

答え3

問題は、シェルコードと予想コードを混在させることで発生します。これは非常に簡単です。 Expectにコードを書くだけです。

#!/usr/bin/expect -f
set timeout 15
set fh [open "input.txt" r]
while {[gets $fh line] != -1} {
    # using a regular expression allows a colon in the password.
    regexp {^([^:]+):(.*)} $line -> host passwd

    spawn ssh user@$host
    expect {
        "yes/no"    { send "yes\r"; exp_continue }
        "Password:" { send "$passwd\r" }
    }
    expect -re "day: $" { send "\r" }
    expect ":"          { send "\r" }
    expect -re "# $"    { send "date\r" }
    expect -re "# $"    { send "exit\r" }
    expect eof
}
close $fh

関連情報