Objective: Use a shell script to upload or download a file via FTP.
FTP client programs typically read the password from a tty
(eg. keyboard) device. To automate the FTP process, we will need to perform some redirection. Below is a simple shell script that does a file upload to a FTP server.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#!/bin/sh HOST="ftphost" USER="username" PASSWD="password" DIR="/remote/upload/directory/path" FILE="foo.bar" ftp -n $HOST <<EOF user $USER $PASSWD verbose cd $DIR put $FILE bye EOF |
The lines between <<EOF
and EOF
are FTP commands. The last FTP command is a bye
command that will close the FTP connection.
To do a file download instead of a upload, simply change the put
command to a get
command and other commands as necessary.
The problem with this script is that it is not that easy to determine if the file has been transferred successfully. The exit status of FTP client programs usually return 0 whether or not the transfer is successful.
To determine if a file transfer is successful, I usually redirect the output of the ftp program and look for FTP server return code of 226. Note that this might not be a foolproof method. Make sure that you test it before using it. The modified shell script is below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#!/bin/sh HOST="ftphost" USER="username" PASSWD="password" DIR="/remote/upload/directory/path" FILE="foo.bar" # called when transfer is ok transfer_ok () { echo transfer ok } # called when transfer is not ok transfer_nok () { echo transfer not ok } ftp -n $HOST >ftp.log 2>ftp_error.log <<EOF user $USER $PASSWD verbose cd $DIR put $FILE bye EOF # check if transfer is ok grep -q ^226 ftp.log EXITSTATUS=$? # transfer_ok called if status 0, else transfer_nok is called [ $EXITSTATUS -eq 0 ] && transfer_ok [ $EXITSTATUS -ne 0 ] && transfer_nok |
The improved script above has 2 functions: transfer_ok
and transfer_nok
. If One of them will be called depending on the contents of the ftp command output. I would say that this is also not a foolproof method – the FTP server can send a 226 reply in response to a FTP abort command as well. But in such scenarios, typically the FTP server will send a 426 (Connection closed; transfer aborted) reply first followed by a 226 reply. Another way to verify that a file transfer is successful is to transfer the file back and check the md5sum
of the file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$ ./ftpcopy.sh transfer ok $ cat ftp.log user user password verbose Verbose mode on. cd /remote/upload/directory/path 250 CWD command successful put foo.bar 200 PORT command successful 150 Opening BINARY mode data connection for foo.bar 226 Transfer complete 11 bytes sent in 0.00018 seconds (61111 bytes/s) bye 221 Goodbye. |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ ./ftpcopy.sh transfer not ok $ cat ftp.log user user password verbose Verbose mode on. cd /remote/upload/directory/path put foo.bar 200 PORT command successful 550 foo.bar: Operation not permitted bye 221 Goodbye. |
An alternative and better method is to use the lftp
command instead of the ftp command in the shell script. If you are on Linux and if lftp
is not installed, install the lftp
package using your default package manager.
1 2 |
# on Debian based Linux distributions $ sudo apt-get install lftp |
1 2 |
# on RedHat based Linux distributions $ sudo yum install lftp |
lftp
seems to be a bit better than ftp when it comes to exit status codes. There have been cases where people have complained that lftp
will return an exit status of 0 even if a non-existing file is requested to be transferred, but I have never encountered such issues. Again, make sure you test all possible scenarios under your environment before using it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#!/bin/sh HOST="ftphost" USER="username" PASSWD="password" DIR="/remote/upload/directory/path" FILE="foo.bar" # called when transfer is ok transfer_ok () { echo transfer ok } # called when transfer is not ok transfer_nok () { echo transfer not ok } lftp <<EOF open $HOST user $USER $PASSWD cd $DIR put $FILE exit EOF # get exit status of lftp EXITSTATUS=$? # transfer_ok called if status 0, else transfer_nok is called [ $EXITSTATUS -eq 0 ] && transfer_ok [ $EXITSTATUS -ne 0 ] && transfer_nok |