如何转义定界符 bash 脚本中的字符

如何转义定界符 bash 脚本中的字符

我有一个 bash 脚本,它读取输入文件并使用heredoc使用expect ssh到服务器,但在转义输入文件中的某些特殊字符时遇到问题。这是我所拥有的..


我有一个名为 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

当我运行这个并点击第二组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

您的问题不在于 shell 或此处的文档,而在于 Expect 将 the$def作为变量引用并尝试扩展它。

这里,bar是在 Expect 中设置的变量并在那里展开(这是单引号,shell 不会展开$bar):

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

这给出了与您看到的相同的错误(expect 脚本现在是双引号的,所以$var 在 shell 中展开,与之前不同):

$ 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

使用通过环境传递的变量,您可以将 here-doc 引用起来,这样 shell 就不会扩展 s ,并在需要时$使用 Expect 变量$host和。$pw

答案3

问题来自于混合 shell 代码和期望代码。这项工作非常简单,只需在 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

相关内容