3

I want place a simple bash function in my .bashrc that wraps around the scp command by accepting a 'source' argument and 'destination' argument, and so far have tried both

function send() {
eval "scp $1 user@annoyingly-long-server-name:$2"
}

and

function send() {
scp $1 user@annoyingly-long-server-name:$2
}

...but when I call either of the above a la

send file.txt ~/

I get the error scp: home-directory-on-remote-machine: Operation not supported. After echoing each argument, it seems that the tilde is expanded into the remote machine's home directory before evaluation. How can I prevent this?

Luke Davis
  • 2,548
  • 2
  • 21
  • 43
  • 2
    Not sure that helps, but you might not need `eval`, just `sap $1 user@server:$2` should do the trick :) – Ulysse BN Jul 26 '17 at 04:40
  • I actually already tried that with the same issue; edited the question. – Luke Davis Jul 26 '17 at 04:42
  • 1
    Does this work with explicit names? e.g. `send foo /home/luke/foo` – oz123 Jul 26 '17 at 04:44
  • @Oz123 ...it does, wow! Why would that be/how can I get it to accept the tilde? – Luke Davis Jul 26 '17 at 04:47
  • @LukeDavis, I updated my answer. Can you accept it please? – oz123 Jul 26 '17 at 04:49
  • Instead of `~` try using `$HOME` perhaps... – l'L'l Jul 26 '17 at 04:50
  • BTW, more correct than either of the above is `send() { scp "$1" user@annoyingly-long-server-name:"$2"; }` -- avoiding the `function` keyword makes it portable to baseline POSIX shells, quoting the arguments improves support for filenames with spaces, avoid `eval` moots [all the problems it causes](http://mywiki.wooledge.org/BashFAQ/048). – Charles Duffy Jul 26 '17 at 04:54
  • @Oz123 Edited the question again... figured out where the issue lies, now just want to know how I can supply the function with a tilde `~` and prevent it from getting expanded before getting called. – Luke Davis Jul 26 '17 at 04:54
  • If you want to avoid local expansion of the tilde, quote it at invocation time. That is, `send foo '~/bar'` – Charles Duffy Jul 26 '17 at 04:54
  • 1
    @CharlesDuffy ...or, apparently, escaping the tilde works. Or I can add a line in the function the parses `$2`, and replaces a leading `$HOME` pathname with a tilde `~`. Kind of overkill, but why not. – Luke Davis Jul 26 '17 at 04:57
  • btw, "tilde is expanded into the remote machine's home directory" is what you *want*. What's actually happening is that the tilde is expanding to the *local* machine's home directory, but that home directory is of course not valid for the *remote* machine (which you intend to copy to). – Charles Duffy Jul 26 '17 at 05:08
  • 1
    Oh yup, you're right. And it makes sense that the expansion happens before it is sent as an argument I suppose. `~` is meant to only mean 'this machine's home directory'. Probably the simplest solution is just to escape the `~`. – Luke Davis Jul 26 '17 at 05:10
  • Can you enable verbose logging. Use "scp -v" and see what is happening inside. Might help. – asatsi Jul 26 '17 at 04:45
  • Edited the question again... figured out where the issue lies, now just want to know how I can supply the function with a tilde `~` and prevent it from getting expanded before getting called. – Luke Davis Jul 26 '17 at 04:54

2 Answers2

3

First of all, you can use ssh-keys to prevent typing of passwords.

Before you use your function do just once:

ssh-copy-id remoteHostName

It is considered better to use keys instead of passwords for ssh and that is true for scp.

Second, you don't need eval.

function send() {
    scp "$1" user@annoyingly-long-server-name:"$2"
}

And finally, you need to use explicit path names:

send foo /home/luke/foo

Because ~ is some how not properly evaluated to /home/luke/.

update, side story:

If your motivation for writing the function send is really that annoyingly-long-server-name you should know about /home/luke/.ssh/config. Inside this file you can do wonders:

Host a-nicer-alias
    Hostname stupid-host-name.verylongdoamin.com
    User luke

Then you can simply do scp a-nicer-alias

oz123
  • 27,559
  • 27
  • 125
  • 187
  • 1
    See my edit; without the `eval` command, I get the same issue unfortunately. And I'm aware of ssh-keys, this was just a quick example. – Luke Davis Jul 26 '17 at 04:43
  • With the missing quotes, this will misbehave with filenames with spaces (and filenames that can be expanded as glob expressions and some other cases). Really should be `scp "$1" user@host:"$2"`. – Charles Duffy Jul 26 '17 at 04:55
1

I figured out a way to convert back from the expanded tilde to the string ~ inside the function using this thread. The function is now

function send() {
dest="$2"
dest="${dest/#$HOME/\~}"
scp $1 user@annoyingly-long-server-name:$dest
}

In the second line, if the string "$HOME" appears at the start of the second argument, it is replaced by a tilde ~. If the source and destination have identical $HOMEs and the user actually did supply the destination path explicitly, it won't do any harm to convert to ~, but if they do not have identical $HOMEs, it fixes the problem.

For some reason it seemed that I had to assign $2 to a variable before performing the string replacement.

Luke Davis
  • 2,548
  • 2
  • 21
  • 43