Shell Script) How can i pass parameters to expect script? - shell

Now I'm making a shell script with expect to send a file to sftp server.
But when i pass a filepath to shell script, there is an empty file path parameter when use 'put' command. (also get or put)
How can I pass parameters to get or put command?
There is a code below.
set FILEPATH [lindex $argv 0]
expect << EOL
sleep 3
spawn sftp id#sftp_ip
expect "password:"
send "mypassword\n"
expect "sftp>"
send "cd /remote/directory\n"
expect "sftp>"
send "get $FILEPATH\n"
expect "sftp>"
send "exit\n"
interact

Related

bash script which downloads a file with sftp

I need to make a sftp connection with a password and download a file. There is an ip restriction so first of all i should make a ssh connection. I wrote a script but it stucks after connecting with ssh.
Note: i also tried doing it with an expect script but it also didn't work.
#!/usr/local/bin/
ssh test#test1.t.com
lftp sftp://test2:123456#test2.com
get "file.xls"
Edit: You can also see my expect code here.
#!/usr/local/bin/expect -f
expect -c "
spawn ssh test#test1.t.com
expect \"test\#test1\:\~\$\"
spawn sftp test2#test2.com
expect \"*assword:\"
send \"123456\r\"
expect \"sftp\>\"
send \"get file.xls\r\"
expect \"sftp\>\"
exit 1
";
I'm not sure exactly what you're trying to accomplish here. First, I'll address the problems in your expect script. Since your shebang line invokes expect, you don't need to wrap the expect body in a call to expect. That gets rid of all the backslashes. Next, you have 2 spawn calls, which raises questions about you're intent. I'm going to assume that you want to ssh to test1, then grab the file from test2 so the file exists on test1. This assumption changes the 2nd spawn to a plain send command.
#!/usr/local/bin/expect -f
set shell_prompt "test#test1:~$"
set sftp_prompt "sftp>"
spawn ssh test#test1
expect $shell_prompt
send "sftp test2#test2\r"
expect "*assword:"
send "123456\r"
expect $sftp_prompt
send "get file.xls\r"
expect $sftp_prompt
send "exit\r"
expect $shell_prompt
send "exit\r"
expect eof
Now, you can scp the file to your local machine. Let's put those 2 steps into one shell script:
#!/bin/sh
expect <<'EXPECT_SCRIPT'
set shell_prompt "test#test1:~$"
set sftp_prompt "sftp>"
spawn ssh test#test1
expect $shell_prompt
send "sftp test2#test2\r"
expect "*assword:"
send "123456\r"
expect $sftp_prompt
send "get file.xls\r"
expect $sftp_prompt
send "exit\r"
expect $shell_prompt
send "exit\r"
expect eof
EXPECT_SCRIPT
scp test#test1:file.xls .

BASH: Send directory files from server(non-sftp) to remote server that use sftp

I can send files one by one by changing the file name, the files are increasing in number so I tried using for loop but it did not work.
I need to send all the files in FILE variable to the remote machine USER#$HOST:/Semi/KFILE-ENG/ELD. Also, the remote machine is sftp enabled, while the sender is using ftp only.
This is what I only have.
HOST=ftpabc.abc.cn
PORT=22
USER=user
PASSWORD=pass
FILE=/u01/flatringDev/ToVendor/LC7/ELD/ELD.7
spawn /usr/bin/sftp $USER#$HOST:/Semi/KFILE-ENG
expect "password:"
send "$PASSWORD\r"
expect "sftp>"
send "mkdir ELD\r"
expect "sftp>"
send "cd ELD\r"
expect "sftp>"
send "put $FILE\r"
expect "sftp>"
send "bye\r"
If someone knows a solution, please let me know. Thank you
#!/bin/bash
HOST=ftpabc.abc.cn
PORT=22
USER=user
PASSWORD=pass
declare -a arry
arry=('file1' 'file2' 'file3' 'file4' '/tmp/kl.pl') # list files with absolute path eg: /tmp/kl.pl
#for FILE in $(ls -1 DIR_containing_files) # If you can all files in same dir, else create an array with elements as filenames with absoulte path and use 2nd for statement
for FILE in ${arry[*]}
do
spawn /usr/bin/sftp $USER#$HOST:/Semi/KFILE-ENG
expect "password:"
send "$PASSWORD\r"
expect "sftp>"
send "mkdir ELD\r"
expect "sftp>"
send "cd ELD\r"
expect "sftp>"
send "put $FILE\r"
expect "sftp>"
send "bye\r"
sleep 5
done
Check if this works, you can have a bash function doing all the expect thing and call it in for loop
or an expect script file and call that in bash for

Shell script with SFTP and mget finishing before downloads are complete

Here is my script:
#!/usr/bin/expect
spawn /usr/bin/sftp user#place
expect "password:"
send "mysecret\n"
expect "sftp>"
send "mget dir/*.csv\n";
expect "sftp>"
send "quit\n"
It starts to download but after a few seconds, it sends the quit command and exits the program before the downloads are complete.
The only way I've successfully made it stop is to throw in an "interact" command but because I want to run this as a cron job, I don't want to just spawn processes that end up sitting.
Is there a way to accomplish this?
edit:
Someone will ask why I don't have a key setup... this is the only method allowed to me by the vendor.
Adding the line set timeout -1 appears to have fixed this problem
The whole script should look as such:
#!/usr/bin/expect
set timeout -1
spawn /usr/bin/sftp user#place
expect "password:"
send "mysecret\n"
expect "sftp>"
send "mget dir/*.csv\n";
expect "sftp>"
send "quit\n"
Well, if you can use Perl:
use Net::SFTP::Foreign;
my $sftp = Net::SFTP::Foreign->new('user#place', password => 'mysecret');
$sftp->mget("dir/*.csv", '.',
on_error => sub {
warn "unable to copy '$_[1]{filename}':".$sftp->error."\n";
});
You will have to install Net::SFTP::Foreign and IO::Pty (also available as packages in several linux distributions).

How to use Bash script variables in Expect conditional statements

I am writing a Bash script and using Expect to do sftp. Now in the Expect block I want to access a Bash variable in a conditional statement. But, I am unable to do so. How can do this?
Also, the execution of this script is controlled from a C program and I want redirect the output to a log file (which again is dynamic). Can I do that and suppress all the output on standard output.
Here is the code:
!/usr/bin/bash
host=$1
user=$2
pass=$3
action=$4
path=$5
echo "Starting...."
function doAction {
strAction="\""$action"\""
echo $strAction
/usr/bin/expect <<EOF > logfile.txt
**set bashaction $strAction**
spawn sftp $user#$host
expect "password:"
send "$pass\r"
expect"sftp>"
send "cd $path\r"
**if {$bashaction == "TEST"} {**
expect "sftp>"
send "prompt\r"
}
expect "sftp>"
send <sftp command>
expect "sftp>"
send_user "quit\n"
exit
EOF
}
doAction
echo "DONE....."
For 1. using an Expect script instead worked.
For the logging issue, using log_user 0 and log_file -a <file> helped.
You don't need to use Bash. Expect can handle all that:
#!/usr/bin/expect
set host [lindex $argv 0]
set user [lindex $argv 1]
set pass [lindex $argv 2]
set action [lindex $argv 3]
set path [lindex $argv 4]
puts "Starting...."
puts "\"$action\""
spawn sftp $user#$host
expect "password:"
send "$pass\r"
expect"sftp>"
send "cd $path\r"
if {$action == "TEST"} {
# Do something
} else {
# Do something else
}
expect "sftp>"
send_user "quit\r"
puts "DONE....."
Coming from Bash, the Tcl/Expect syntax is a little strange, but you should not have any problem expanding the above skeleton.
Accessing Environment Variables from TCL and Expect
Since you are calling this Expect script from another process, you can make use of environment variables. For example, if your parent process has exported action to the environment, then you can access its value within your expect script with:
$::env(action)
In Bash, you can mark the variable for export with the export builtin. For example:
export action
Since I'm not sure how you're invoking the Expect script from C, it's up to you to make sure the variable is properly exported.
Disable Logging to Standard Output
To disable logging to standard output from spawned processes, Expect provides the log_user command. You can prevent your spawned processes from writing to stdout with log_user 0.
The expect(1) manual says:
By default, the send/expect dialogue is logged to stdout (and a
logfile if open). The logging to stdout is disabled by the command
"log_user 0" and reenabled by "log_user 1". Logging to the logfile
is unchanged.
This doesn't actually close standard output, which is generally not what you want anyway. Doing so will cause anything that writes to stdout to throw an error like this:
can not find channel named "stdout"
while executing
"puts hello"
(file "/tmp/foo" line 8)
To suppress output to the standard output you can use
command here >/dev/null 2>/dev/null
To write to a log file, you can use similar piping (> or >>), or the tee command if you want to write the output in the middle of a long pipe.

Pipe stdin into "expect" script

I am uploading a file via ftp using expect. The file is piped into my bash script.
#!/bin/bash
HOST='example.com'
USER='XXX'
PASSWD='XXX'
expect << EOT
spawn ftp $HOST
expect "Name*:"
send "$USER\r"
expect "Password:"
send "$PASSWD\r"
expect "ftp>"
send "binary\r"
expect "ftp>"
send "prompt\r"
expect "ftp>"
send "put - $1\r" ####
expect "ftp>"
send "bye\r"
expect eof
EOT
On the highlighted line I want ftp to get access to the main script stdin.
Thank you
I believe the key to what you are looking for is Expect's interact command. You'd get a script like this:
#!/usr/bin/env expect
# Set these in the Tcl way, not the bash way
set HOST "example.com"
set USER "XXX"
set PASSWD "YYY"
# You should recognize this bit...
spawn ftp $HOST
expect "Name*:"
send "$USER\r"
expect "Password:"
send "$PASSWD\r"
expect "ftp>"
send "binary\r"
expect "ftp>"
send "prompt\r"
expect "ftp>"
# New stuff starts here
send "put - [lindex $argv 0]\r"
interact {
"ftp>" { return }
}
send "bye\r"
wait
exit
I've rewritten your script so it doesn't use a here document, because that would have interfered with the reading of the content (here-docs are presented as stdinā€¦) and switched it to use a few more idiomatic ways of doing things (idiomatic argument access being the main one).
That said, if I was doing this sort of thing for real, I'd look into using the ftp package from Tcllib as that talks the protocol directly instead of using a possibly-problematic subprocess. (Indeed, if you were going to be doing this on Windows, you'd have to do it that way because of quirks of how Expect and FTP.EXE work on that platform).

Resources