3

I have this files:

A.class
A$1.class
A$2.class

And a bash script with something like:

for i in *; do
    scp "${i}" foo@bar:/tmp/;
done

This works fine. All three files are copied. The problem is with the following:

for i in *; do
    scp "${i}" foo@bar:/tmp/"${i}";
    scp "${i}" foo@bar:"/tmp/${i}";
    scp "${i}" "foo@bar:/tmp/${i}";
done

In any of this cases, SCP copies all the files, but to the same file A.class.

Any ideas? Thanks!

Eldelshell
  • 139
  • 6

3 Answers3

6

Using scp -v shows some details about how scp works:

debug1: Sending command: scp -v -t /tmp/A$2.class
Sending file modes: C0644 0 A$2.class
Sink: C0644 0 A$2.class
A$2.class                                     100%    0     0.0KB/s   00:00    

Essentially, since scp runs over ssh, it needs a shell to execute your command, and since the $ is not escaped, the remote shell interprets $2 as an empty string, which makes the target name A.class.

There are two ways you could go about fixing this. First, you could just use the * glob in your scp command directly:

scp * foo@bar:/tmp/

But I'm pretty sure you have some other need to use a loop, so here's how to fix it in the loop:

for i in *; do
    scp "${i}" "foo@bar:'/tmp/${i}'"
done

Checking the output of scp -v now shows:

debug1: Sending command: scp -v -t '/tmp/A$2.class'

Success! The single quotes will prevent the remote shell from performing variable expansion.

UPDATE: Since the argument is interpreted by the shell as an argument, you can do things like this:

scp foo@bar:'$(which ssh)' .

Or even more complicated shell pipelines that result in a filename. This is not a security issue, since if you can connect with scp, you can connect with ssh. But it might make things more convenient for someone.

bonsaiviking
  • 4,420
  • 17
  • 26
  • Sir, awesome. Works like a charm. Didn't test the single quotes solutions assuming it would save the files as ${i} – Eldelshell Apr 11 '12 at 16:59
  • The double quotes "protect" the single quotes from interpretation by your local shell. Another way to do it would be `foo@bar:\'/tmp/${i}\'` – bonsaiviking Apr 11 '12 at 20:34
1

One solution would be to use find for this:

find . -name A\*.class -print0|xargs -0 -i _PLACEHOLDER_ scp _PLACEHOLDER_ foo@bar:/tmp/_PLACEHOLDER_

The idea is to avoid shell expansion.

Mircea Vutcovici
  • 17,619
  • 4
  • 56
  • 83
1

I think this is caused by "${i}" being expanded locally to A$1.class and then sent to the remote which expands it to A.class as the $1 is "". You an get round this with

scp "${i}" foo@bar:/tmp/'${i}';
user9517
  • 115,471
  • 20
  • 215
  • 297