From: Toby Newman on
I'm quite new to tcl and expect.
I am using this expect script to connect to an sshd server:
http://pastebin.com/EpG5jY3f

It's failing when the password begins with a ". (I have to prepend the
" with a backslash at the command prompt or bash trips over it).

Examples:

../expectScript.sh pass\"word 127.0.0.1 tnewman
This works, the " is in the middle of the password.

../expectScript.sh \"password 127.0.0.1 tnewman
This doesn't work, the " is at the start of the password.

Example output:
Logging in using tnewman and {"FFFFFF}...spawn ssh tnewman(a)127.0.0.1
tnewman(a)127.0.0.1's password:
Permission denied, please try again.

How should I be escaping this " so that it is passed along correctly?
My understanding was that anything shown in {} is passed straight
through by tcl.

--
-Toby
Add the word afiduluminag to the subject to circumvent my email filters.
From: Donald Arseneau on
On Mar 29, 7:57 am, Toby Newman <goo...(a)asktoby.com> wrote:

set password [lrange $argv 0 0]
set ip [lrange $argv 1 1]
set username [lrange $argv 2 2]

# " ` and \ in passwords interfere with bash when the command
# is entered so have to be escaped with \.
# Use TCL to strip those backslashes out.
regsub -all {\\\"} $password "\"" password
regsub -all {\\\`} $password "\`" password
regsub -all {\\\\} $password "\\" password

> It's failing when the password begins with a ". (I have to prepend the
> " with a backslash at the command prompt or bash trips over it).
>
> Examples:
>
> ./expectScript.sh pass\"word 127.0.0.1 tnewman
>    This works, the " is in the middle of the password.
>
> ./expectScript.sh \"password 127.0.0.1 tnewman
>    This doesn't work, the " is at the start of the password.
>
> Example output:
> Logging in using tnewman and {"FFFFFF}...spawn ssh tnew...(a)127.0.0.1
> tnew...(a)127.0.0.1's password:
> Permission denied, please try again.
>
> How should I be escaping this " so that it is passed along correctly?
> My understanding was that anything shown in {} is passed straight
> through by tcl.

It sounds like you are confusing bash quoting with Tcl quoting.
Tcl's verbatim quoting with { } is Bash's verbatim quoting with ' '.

Your problem is your extraction of the arguments from the argv list
to produce one-element lists rather than just strings. Change the
[lrange $argv n n] to [lindex $argv n] and get rid of the regsubs.

Here is what is happening.

Bash does its own processing of quotes and backslash-escapes before
handing arguments to Tcl, so the backslash is not in any argument
when Tcl/expect receives it! Your password argument is indeed
pass"word or "password, saved in element 0 of the $argv list.
There should be no need to remove any backslash (no regsub lines).

However, by extracting sub-lists from $argv rather than simply
the items themselves, you get into trouble. When you use these
little lists as if they were the items themselves you get extra
formatting characters inserted by Tcl, including the \ that you
thought came from the original bash argument. For some reason,
when the " is at the beginning, Tcl doesn't make it list-safe using
\ anymore, but by putting { } around the whole argument. Either of
these would behave the same if you went on to use the value as
a list (both [lindex $password 0] or [join $password {}] give the
proper password string), but you use the list as if it were the
password itself.

You can skip the details if they are confusing, but just remember
to use list operations for lists and string commands for strings,
and use [lindex] to extract list items but [lrange] to extract
sub-lists.

Another way to get the arguments, rather than lindex is

lassign $argv password ip username

which works in recent Tcl, or

foreach {password ip username} $argv { break }

a hackish solution that has worked for many years.

Donald Arseneau



From: Toby Newman on
On 2010-03-30, Donald Arseneau <asnd(a)triumf.ca> wrote:
> On Mar 29, 7:57 am, Toby Newman <goo...(a)asktoby.com> wrote:
>
> set password [lrange $argv 0 0]
> set ip [lrange $argv 1 1]
> set username [lrange $argv 2 2]
>
> # " ` and \ in passwords interfere with bash when the command
> # is entered so have to be escaped with \.
> # Use TCL to strip those backslashes out.
> regsub -all {\\\"} $password "\"" password
> regsub -all {\\\`} $password "\`" password
> regsub -all {\\\\} $password "\\" password
>
>> It's failing when the password begins with a ". (I have to prepend the
>> " with a backslash at the command prompt or bash trips over it).
>>
>> Examples:
>>
>> ./expectScript.sh pass\"word 127.0.0.1 tnewman
>>    This works, the " is in the middle of the password.
>>
>> ./expectScript.sh \"password 127.0.0.1 tnewman
>>    This doesn't work, the " is at the start of the password.
>>
>> Example output:
>> Logging in using tnewman and {"FFFFFF}...spawn ssh tnew...(a)127.0.0.1
>> tnew...(a)127.0.0.1's password:
>> Permission denied, please try again.
>>
>> How should I be escaping this " so that it is passed along correctly?
>> My understanding was that anything shown in {} is passed straight
>> through by tcl.
>
> It sounds like you are confusing bash quoting with Tcl quoting.
> Tcl's verbatim quoting with { } is Bash's verbatim quoting with ' '.
>
> Your problem is your extraction of the arguments from the argv list
> to produce one-element lists rather than just strings. Change the
> [lrange $argv n n] to [lindex $argv n] and get rid of the regsubs.
>
> Here is what is happening.
>
> Bash does its own processing of quotes and backslash-escapes before
> handing arguments to Tcl, so the backslash is not in any argument
> when Tcl/expect receives it! Your password argument is indeed
> pass"word or "password, saved in element 0 of the $argv list.
> There should be no need to remove any backslash (no regsub lines).
>
> However, by extracting sub-lists from $argv rather than simply
> the items themselves, you get into trouble. When you use these
> little lists as if they were the items themselves you get extra
> formatting characters inserted by Tcl, including the \ that you
> thought came from the original bash argument. For some reason,
> when the " is at the beginning, Tcl doesn't make it list-safe using
> \ anymore, but by putting { } around the whole argument. Either of
> these would behave the same if you went on to use the value as
> a list (both [lindex $password 0] or [join $password {}] give the
> proper password string), but you use the list as if it were the
> password itself.
>
> You can skip the details if they are confusing, but just remember
> to use list operations for lists and string commands for strings,
> and use [lindex] to extract list items but [lrange] to extract
> sub-lists.
>
> Another way to get the arguments, rather than lindex is
>
> lassign $argv password ip username
>
> which works in recent Tcl, or
>
> foreach {password ip username} $argv { break }
>
> a hackish solution that has worked for many years.
>
> Donald Arseneau

Thank you for that clear solution - you helped me solve my problem.

--
-Toby
Add the word afiduluminag to the subject to circumvent my email filters.