"One shot" sftp (simulating scp)


Normally if you want to quickly send a file to a remote directory on an ssh server (as a one liner or within a script), the following would suffice.


$ scp file server:path

However, you may occasionally come across a server that only has sftp enabled and not scp. For example if you tried to scp files to an OpenSSH server configured as follows.


Subsystem sftp internal-sftp

Match User yourusername
  ChrootDirectory /home/%u
  ForceCommand internal-sftp

You would get the following the error message,


This service allows sftp connections only.


In such a case, you can still simulate basic scp behaviour as follows.


$ echo put file | sftp server:path

Alternatively (if your shell supports it) you could use a 'here-string' and save yourself a couple of characters.


$ sftp server:path <<< put\ file

If you needed to upload multiple files, you could use a little loop.


$ for f in file1 file2 file3; do echo put "$f" | sftp server:path; done

ℹ This makes every put request a new connection to the server. So if you do this with 'interactive authentication' (password-based), it is going to be very annoying as you will continually have to re-enter your password for each file upload. In such cases, switch to non-interactive authentication (if possible) or just do it the classic sftp way and save yourself a bunch of hassle. You might also want to skip the rest of this post. 😉


The above example can be further improved with escaped quoting, so that you can use wildcards and not have to worry about spaces and other unusual characters in the file names.


$ for f in file* ; do echo put "\"$f\"" | sftp server:path; done

At this point it is probably a good idea to combine all of the above and make a very basic upload script.


#!/bin/sh
s="$1"
shift 1
for f in "$@"; do
  echo "put \"$f\"" | sftp "$s" || exit 1
done

Give this a suitable name (e.g. 'xsftp') and use it as follows.


$ xsftp server:path file*

Using bash instead of bourne shell would allow you to specify 'server:path' as the last option, rather than the first (just like scp), i.e.


$ xsftp file* server:path 

To do that, here is the script above rewritten with some 'bashisms'.


#!/bin/bash
eval s=\$$#
for f in "${@:1:$(($#-1))}"; do
  echo "put \"$f\"" | sftp "$s" || exit 1
done

And that is it. I hope this is useful to someone. If not… 🤷🏼



[✍ 2022-01-16 12:27] Here is a follow up post with a better version.


"One shot" sftp (a step further)



📝 Comment

🔙 Gemlog index

🔝 Capsule index



/gemlog/