I have the following function. It tests for a SFTP server connection. It does what it's supposed to, but it has one edge-case that's causing me a headache. When the variable SFTP_SERVER_PASSWORD contains a dollar-sign it won't work because the expect script thinks it has to search for a variable.
function test_sftp_connection() {
expect << EOF
log_user 1
spawn -noecho sftp -P $SFTP_SERVER_PORTNUMBER $SFTP_SERVER_USERNAME@$SFTP_SERVER_HOSTNAME
expect "password:"
send "$SFTP_SERVER_PASSWORD\r"
expect {
"sftp>" {
send "exit\r"
expect eof
exit 0
}
timeout {
exit 1
}
}
EOF
}
I tried various solutions:
send $SFTP_SERVER_PASSWORD"\r"send "$SFTP_SERVER_PASSWORD\r"send "'$SFTP_SERVER_PASSWORD'\r"
But they all end up with the same error:
'can't read "[few letters after dollar-sign in password]": no such variable'.
I guess this is hard because it is using a Bash variable in an expect script. But I still think this should be solvable. Does anyone have any ideas on how to solve this?
From the viewpoint of the Tcl language:
You have to escape the
$if you want to send a $ literally:However your case is complicated in that you pass to the Tcl program a string, which undergoes parameter expansion by the shell. This means that if you have a shell variable
SFTP_SERVER_PASSWORD, and the variable contains a$, there would be no escaping.One solution would be to replace every
$inside the shell variable by the string\$. However in my opinion, this is cumbersome and error prone. A similar possibility would be to place the shell variable into the environment, before calling Tcl, i.e.and then in your Tcl script do a
This also fixes the danger of code injection. In your original approach, a password containing inside the string
[foo], would try to execute the Tcl commandfoo, and if the string between the brackets happens to be a valid Tcl function, it could execute arbitrary Tcl code under the hood.Note that the backslash in front of the
$envhas the purpose to tell the shell NOT to interpretenvas shell variable. This is necessary because you have the complete Tcl program included in a double quoted context.If you find this escaping ugly (as I would), you can ask the shell to completely keep its fingers from the Tcl text. Right now, you are "using" the shell to expand the variables in the
spawncommand. This is fine, since these variables won't contain a white space or a character which is special to Tcl, but still I would decouple here Tcl and shell completely:If you place those variables into the environment as well and refer to then via
$env(....)inside Tcl, you can put your whole Tcl program into single quoted context (or perhaps into its own file, with a#!/usr/bin/expectline on top and don't have to worry about shell issues anymore.