Como escapar de um personagem em um script heredoc bash

2

Eu tenho um script bash que lê um arquivo de entrada e usa um heredoc para ssh em um servidor usando o expect, mas estou tendo problemas para escapar de algum caractere especial no arquivo de entrada. Aqui está o que eu tenho ..

Eu tenho uma chamada de arquivo input.txt que contém:

1.2.3.4:abcdefg
2.3.4.5:abc$def

Eu tenho um script bash que se parece com isso. Ele funciona bem se a senha não contiver o caractere '$', mas explodir quando a senha contiver '$', pois trata a parte depois de $ como uma variável.

#!/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

Eu recebi o seguinte erro quando eu corri isso e acertei o segundo conjunto de 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)

Alguém tem alguma ideia? Eu tentei citação simples dentro de aspas duplas e tudo que eu conseguia pensar, mas ainda não consigo fazê-lo funcionar. Agradecemos antecipadamente

    
por quan3t 14.08.2018 / 22:26

3 respostas

3

Seu problema não é com o shell, ou com o doc aqui, mas com Expect, pegando a $def como referência de variável e tentando expandi-la.

Aqui, bar é uma variável definida no Expect e expandida lá (isso é simples, o shell não expande $bar ):

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

Isto dá o mesmo erro que você viu (o script expect é citado duas vezes agora, então $var faz expandir no shell, ao contrário do anterior):

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

Você precisaria escapar do $ , mas também precisaria escapar pelo menos de [ , pois também é especial em Expectar, ou melhor, no Tcl, no qual o Expect se baseia. Talvez haja outros também, não tenho certeza ...

Pode ser mais fácil transmitir o valor por meio de um argumento de linha de comando:

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

Ou através do ambiente:

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

Um pouco mais perto do que seu código faz:

#!/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

Com as variáveis passadas pelo ambiente, você pode fazer o aqui-doc citado de modo que o shell não expanda o $ s e use as variáveis esperadas $host e $pw quando necessário.

    
por 15.08.2018 / 02:28
2

Os problemas vêm da mistura do código shell e esperam o código. Este trabalho é simples o suficiente para codificar apenas em esperar:

#!/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
    
por 15.08.2018 / 15:28
0

Se você não quiser que nenhuma variável seja expandida dentro do documento aqui, cite o EOD:

cat << 'EOD'
...
EOD

Por exemplo, este arquivo:

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

produz este resultado:

test$literal
varthis
    
por 14.08.2018 / 22:29