Bash – Convert password with special characters for use with expect script

bashexpectregular expressionsftp

I have an expect script that connects to an SFTP site and uploads some files. The ID and password are contained in local text files. The expect script reads those files in as a variable and then passes it to the SFTP server.

The problem I am experiencing is we are being provided passwords with complexity that includes special characters ($\,!) etc. For example if the password is pass$word the $ passes as a special rather than a literal. Being this is being passed through a variable (multiple sites and IDs)

I am unable to use '$pw' as it will literally pass $pw to the server and using "$pw" sends special characters. I need it to pass the password exactly as is.

I have been escaping them out by hand so far (e.g. pass\$word) but that is tedious and I would like to do it script-o-matically.

The script looks like this (names and places changed to protect the innocent)

#!/bin/bash

home=/data/ftp/vf/drop/
un=`cat /home/vf/workflow/.udf/.u/.u$1`
pw=`cat /home/vf/workflow/.udf/.p/.p$1`
sl=`cd /home/vf/workflow/scheduler; grep $1 upload*|cut -d \: -f1`

/usr/bin/expect -c "
spawn /usr/bin/sftp -o KexDHMin=1024 $un@$sl.mysteriouslocation.com
set timeout -1
expect *Authentication
expect Password*
send \"$pw\r\"
expect sftp*
send \"mput /$home/$1/transfer/*\r\"
expect sftp*
send \"ls \r\"
expect sftp*
send \"bye\r\"
expect eof
exit

How can I either pass the password to the expect script so that it sends the literal characters and not give them special meaning?
I don't think I can script a "fix" for the password file itself, meaning go in and every time it sees a special character, escape it out via sed or other means as the escape character itself is special and could end up in a loop.

Your help is greatly appreciated!

Best Answer

A small Perl snippet can be used to escape (backslash) all ASCII characters not matching /[A-Za-z_0-9]/ by using the quotemeta function. The Perl command line is easily incorporated into your shell script:

#/bin/sh
pstr='$x%y!zpass' # plain, un-escaped string
estr=$(perl -e 'print quotemeta shift(@ARGV)' "${pstr}")
echo ${estr}      # show escaped string

produces:

\$x\%y\!zpass
Related Question