Take the 2-minute tour ×
Server Fault is a question and answer site for system and network administrators. It's 100% free, no registration required.

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!

share|improve this question
1  
If the first solution works fine, what is the problem ? –  adaptr Apr 11 '12 at 14:33
    
We need to send the full path of the files. –  Eldelshell Apr 11 '12 at 17:00

3 Answers 3

up vote 3 down vote accepted

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.

share|improve this answer
    
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

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.

share|improve this answer

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}';
share|improve this answer
1  
You could also leave off the second ${i} and just tell scp to put them in the /tmp/ directory with the same name as they have now. –  Ladadadada Apr 11 '12 at 14:33
    
@Ladadadada: yes, the OP says this in the question! –  Iain Apr 11 '12 at 14:34
    
Ah... so he does. Wait... what's the problem again? –  Ladadadada Apr 11 '12 at 14:36
    
Understanding the difference I guess. –  Iain Apr 11 '12 at 14:37

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.