An Easy Way to Put Variables in an Expect Script for SFTP

Article ID: 57615

Q: I've read your articles about using OpenSSH on IBM i and your articles discussing how to use Expect to automate the process when an interactive password is required. So far, so good! But now I'd like to insert variables into my Expect script. The file name that I have to retrieve is based on the current date, so I can easily write code to calculate what the file name should be, but how do I insert it into my Expect script?

A: I'd recommend writing a shell script to build your Expect script on the fly. One nice thing about Expect is that it can read its script from a pipe instead of a file. That makes it easy to build the script on the fly, because there's no need to write it to a temporary file. This article shows you how to do that.

It's easy to reference an environment variable in a shell script; all you have to do is precede the variable name with a dollar sign. It's also easy for a CL program to set environment variables; therefore, they make it easy to pass information to the script.

So here's a quick-and-dirty CL program. I'm just hard-coding values for the environment variables for now, but you will want to replace the hard-coded values with CL variables that contain the name of the file that you've calculated.

PGM

   ADDENVVAR REPLACE(*YES) ENVVAR(USER)     VALUE('klemscot')
   ADDENVVAR REPLACE(*YES) ENVVAR(SFTPHOST) VALUE('whatever.example.com')
   ADDENVVAR REPLACE(*YES) ENVVAR(PASSWORD) VALUE('bigboy')
   ADDENVVAR REPLACE(*YES) ENVVAR(GETFILE)  VALUE('STL-20081212.csv')

   CALL QP2SHELL PARM('/qopensys/usr/bin/sh' +
                      '/home/klemscot/myexp.sh')

   DLTOVR *ALL

ENDPGM

Like I said, I'll leave it up to you to figure out how to calculate the file name. Once the file name is calculated, it should be possible to replace the VALUE('STL-20081212.csv') with VALUE(&SOMEVAR). So you can calculate the file name and store it in &SOMEVAR then have CL set the GETFILE environment variable, as shown.

Obviously, you'll also have to change the user ID, password, host, and so forth to whatever is appropriate. And I've placed my script in the /home/klemscot directory, but you'll probably want to create one that's available system-wide and put it in /usr/local/bin or some such location.

Anyway, the preceding CL program does nothing but set a few variables and then invoke a shell script. In my example, I called the shell script myexp.sh, but you could name that whatever is appropriate for you.

The shell script itself does the bulk of the work and looks like this:

#!/bin/sh

build_script() {

cat <<End-of-message
#!/usr/local/bin/expect -f
spawn sftp ${USER}@${SFTPHOST}
expect "connecting (yes/no)?" { send "yes\n"; expect "Password:" }        "Password:"
send "${PASSWORD}\n"
expect "sftp>"
send "get ${GETFILE}\n"
expect "sftp>"
send "quit\n"
exit
End-of-message

}

build_script | /usr/local/bin/expect -f -

The build_script() thing is a "function" (equivalent to an RPG subprocedure) that does nothing but build an Expect script. You'll see that the expect script looks very much like other Expect script examples you've seen. The main difference here is that it's produced in a here doc.

The <<End-of-message tells the script that everything up until the "End-of-message" string should be considered data that's sent to the cat utility. The cat utility does nothing except take that data and print it out.

In case it's not clear, the strings like ${USER} and ${PASSWORD} are actually variable replacements. The script will get USER variable set by the CL program and insert it where it says ${USER}. . . the same goes for the other ${XXX} strings.

At the bottom of the script, I have build_script | $EXPECT -. The pipe (vertical line) tells the shell to send the output from build_script (the stuff printed by cat) to the input of the expect program. The -f means to read its input from a file--and the file name of - means "read from stdin" (i.e., read from the pipe, so it gets the data written by the build_script routine and runs it as if it's an expect script).

So, the build_script() function fills in the variables, and the output is sent directly to the expect tool, which runs it as a script.

Since this is to be run by QP2SHELL, make sure your script is coded with a CCSID of 819, and with *LF (not *CRLF!) as the end-of-line delimiter. Good luck!

ProVIP Sponsors

ProVIP Sponsors