73

I'm trying to write a Bash script that uploads a file to a server. How can I achieve this? Is a Bash script the right thing to use for this?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Andrew
  • 227,796
  • 193
  • 515
  • 708

11 Answers11

100

Below are two answers. First is a suggestion to use a more secure/flexible solution like ssh/scp/sftp. Second is an explanation of how to run ftp in batch mode.

A secure solution:

You really should use SSH/SCP/SFTP for this rather than FTP. SSH/SCP have the benefits of being more secure and working with public/private keys which allows it to run without a username or password.

You can send a single file:

scp <file to upload> <username>@<hostname>:<destination path>

Or a whole directory:

scp -r <directory to upload> <username>@<hostname>:<destination path>

For more details on setting up keys and moving files to the server with RSYNC, which is useful if you have a lot of files to move, or if you sometimes get just one new file among a set of random files, take a look at:

http://troy.jdmz.net/rsync/index.html

You can also execute a single command after sshing into a server:

From man ssh

ssh [...snipped...] hostname [command] If command is specified, it is executed on the remote host instead of a login shell.

So, an example command is:

ssh username@hostname.example bunzip file_just_sent.bz2

If you can use SFTP with keys to gain the benefit of a secured connection, there are two tricks I've used to execute commands.

First, you can pass commands using echo and pipe

echo "put files*.xml" | sftp -p -i ~/.ssh/key_name username@hostname.example

You can also use a batchfile with the -b parameter:

sftp -b batchfile.txt ~/.ssh/key_name username@hostname.example

An FTP solution, if you really need it:

If you understand that FTP is insecure and more limited and you really really want to script it...

There's a great article on this at http://www.stratigery.com/scripting.ftp.html

#!/bin/sh
HOST='ftp.example.com'
USER='yourid'
PASSWD='yourpw'
FILE='file.txt'

ftp -n $HOST <<END_SCRIPT
quote USER $USER
quote PASS $PASSWD
binary
put $FILE
quit
END_SCRIPT
exit 0

The -n to ftp ensures that the command won't try to get the password from the current terminal. The other fancy part is the use of a heredoc: the <<END_SCRIPT starts the heredoc and then that exact same END_SCRIPT on the beginning of the line by itself ends the heredoc. The binary command will set it to binary mode which helps if you are transferring something other than a text file.

Neuron
  • 5,141
  • 5
  • 38
  • 59
greggles
  • 2,089
  • 5
  • 20
  • 38
  • I would like to do this! Can you please expand on how I can do this? I need to do some things with ssh after uploading the file. Can this be done in one session? – Andrew Dec 12 '09 at 21:46
  • 17
    While it was useful advice for the OP, it shouldn't be the accepted answer. It doesn't answer the original question (that is found via Google) – Lukas Eder Apr 14 '15 at 10:29
  • 2
    Some of the other posters have added binary mode (i.e. `bin`) to the FTP commands. Since yours is the accepted answer, I recommend you adding this to your answer too. – Stephen Quan Feb 10 '17 at 00:12
  • Thanks for the answer including both ssh and FTP. I don't understand the fuss about FTP. Maybe I'm working for a client who's website is on FTP and I just don't care, I care only about what the client wants to have done with his website. – Maciej Krawczyk Jul 10 '18 at 18:18
  • 1
    Can also type `hash` to show progress of the ftp transmission in a way. Also, you will usually see `< – AdamKalisz Dec 12 '18 at 07:33
  • If you need to upload to **another port**, use `scp -P 22 @:` (change 22 to the port you need) – MS Berends Apr 27 '20 at 14:29
  • How about if TLS is required for the ftp script solution? – Alvaro B May 07 '21 at 17:20
  • @AlvaroB I suggest asking that with more detail as a new question. – greggles Sep 27 '21 at 17:39
  • what is the `binary` in the script ? – Sayan Dasgupta Dec 12 '22 at 19:00
  • 1
    @SayanDasgupta as the answer says "The binary command will set it to binary mode which helps if you are transferring something other than a text file." If you are transferring images, pdfs, documents, zips, archives you want binary mode. If you know you are only transferring literal text files (e.g. source code, csvs) then skipping the binary might save you a tiny amount of time. – greggles Dec 13 '22 at 03:07
82

You can use a heredoc to do this, e.g.

ftp -n $Server <<End-Of-Session
# -n option disables auto-logon

user anonymous "$Password"
binary
cd $Directory
put "$Filename.lsm"
put "$Filename.tar.gz"
bye
End-Of-Session

so the ftp process is fed on standard input with everything up to End-Of-Session. It is a useful tip for spawning any process, not just ftp! Note that this saves spawning a separate process (echo, cat, etc.). It is not a major resource saving, but it is worth bearing in mind.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
  • This is totally confusing. How do I get out of this End-Of-Session thing? Why not input directly to the ftp prompt that comes without that? – erikbstack Aug 26 '15 at 09:53
  • 2
    @erikb85 - this for scripts, not (necessarily) for interactive use. The heredoc will automatically register an act upon your 'End-Of-Session' marker (you'd likely use EOF or similar) – Brian Agnew Aug 26 '15 at 13:07
37

The ftp command isn't designed for scripts, so controlling it is awkward, and getting its exit status is even more awkward.

Curl is made to be scriptable, and also has the merit that you can easily switch to other protocols later by just modifying the URL. If you put your FTP credentials in your .netrc, you can simply do:

# Download file
curl --netrc --remote-name ftp://ftp.example.com/file.bin
# Upload file
curl --netrc --upload-file file.bin ftp://ftp.example.com/

If you must, you can specify username and password directly on the command line using --user username:password instead of --netrc.

Pelle Nilsson
  • 970
  • 8
  • 10
20

Install ncftpput and ncftpget. They're usually part of the same package.

Paul Tomblin
  • 179,021
  • 58
  • 319
  • 408
  • 2
    command line is: "ncftpput -u username -p password server.org /path/ filename.blah" note that the connection won't be encrypted and the password will be sent in plain text. – Adam Mar 12 '16 at 13:19
  • 4
    @Adam the "connection won't be encrypted" etc is a consequence of using FTP, which was specified in the question. It's going to happen no matter what FTP client you use. – Paul Tomblin Mar 14 '16 at 01:21
8

The command in one line:

ftp -in -u ftp://username:password@servername/path/to/ localfile
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Chizhik
  • 113
  • 1
  • 1
  • 2
    @erikbwork -u is: " -u URL file [...] - Upload files on the command line to URL where URL is one of the ftp URL types as supported by auto-fetch (with an optional target filename for single file uploads), and file is one or more local files to be uploaded." – schmilblick Aug 18 '17 at 06:14
  • 7
    Which version of ftp are you using? It does not work on ubuntu 18 – synek317 Jan 16 '19 at 10:41
8

Use this to upload a file to a remote location:

#!/bin/bash
#$1 is the file name
#usage:this_script <filename>
HOST='your host'
USER="your user"
PASSWD="pass"
FILE="abc.php"
REMOTEPATH='/html'

ftp -n $HOST <<END_SCRIPT
quote USER $USER
quote PASS $PASSWD
cd $REMOTEPATH
put $FILE 
quit
END_SCRIPT
exit 0
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Shal
  • 613
  • 6
  • 9
4
#/bin/bash
# $1 is the file name
# usage: this_script  <filename>
IP_address="xx.xxx.xx.xx"
username="username"
domain=my.ftp.domain
password=password

echo "
 verbose
 open $IP_address
 USER $username $password
 put $1
 bye
" | ftp -n > ftp_$$.log
ennuikiller
  • 46,381
  • 14
  • 112
  • 137
3

There isn't any need to complicate stuff. This should work:

#/bin/bash
echo "
 verbose
 open ftp.mydomain.net
 user myusername mypassword
 ascii
 put textfile1
 put textfile2
 bin
 put binaryfile1
 put binaryfile2
 bye
" | ftp -n > ftp_$$.log

Or you can use mput if you have many files...

Cerbrus
  • 70,800
  • 18
  • 132
  • 147
3

Working example to put your file on root...see, it's very simple:

#!/bin/sh
HOST='ftp.users.qwest.net'
USER='yourid'
PASSWD='yourpw'
FILE='file.txt'

ftp -n $HOST <<END_SCRIPT
quote USER $USER
quote PASS $PASSWD
put $FILE
quit
END_SCRIPT
exit 0
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Shal
  • 613
  • 6
  • 9
  • #!/bin/bash #$1 is the file name #usage:this_script HOST='yourhost' USER="youruser" PASSWD="yourpass" FILE="abc.php" REMOTEPATH='/html' ftp -n $HOST < – Shal Apr 29 '15 at 07:27
0

If you want to use it inside a 'for' to copy the last generated files for an everyday backup...

j=0
var="`find /backup/path/ -name 'something*' -type f -mtime -1`"
# We have some files in $var with last day change date

for i in $var
  do
  j=$(( $j + 1 ))
  dirname="`dirname $i`"
  filename="`basename $i`"
  /usr/bin/ftp -in >> /tmp/ftp.good 2>> /tmp/ftp.bad << EOF
    open 123.456.789.012
    user user_name passwd
    bin
    lcd $dirname
    put $filename
    quit
  EOF      # End of ftp
done       # End of 'for' iteration
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
oso_togari
  • 19
  • 7
0
echo -e "open <ftp.hostname>\nuser <username> <password>\nbinary\nmkdir New_Folder\nquit" | ftp -nv
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131