Search code examples
perlbashunixsshscp

scp with special characters programmatically


I have been searching for this for a while, and can't find a satisfactory answer.

I have a perl script that needs to copy a file from one host to another, essentially

sub copy_file{
    my($from_server, $from_path, $to_server, $to_path, $filename) = @_;

    my $from_location = "$from_server:\"\\\"${from_path}${filename}\\\"\"";
    my $to_location = $to_path . $filename;
    $to_location =~ s/\s/\\\\ /g;
    $to_location = "${to_server}:\"\\\"${to_location}\\\"\"";

    return system("scp -p $from_location $to_location >/dev/null 2>&1"");
}

The problem is, some of my filenames look like this:

BLAH;BLAH;BLAH.TXT
Some really nicely named file( With spaces, prentices, &, etc...).xlx

I am already handling whitespaces, and the code for that is quite ugly since on each side, the files could be local or remote, and the escaping is different for the from and to part of the scp call.

what I am really looking for is either to somehow to escape all possible special characters or somehow bypass the shell expansion entirely by using POSIX system calls. I am ok with writing a XS Module if need be.

I have the correct keys set up in the .ssh directory Also I am not honestly sure which special characters do and don't cause problems. I would like to support all legal filename characters.


Solution

  • Say you want to copy file foo(s) using scp.

    As shown below, scp treats the source and target as shell literals, so you pass the following arguments to scp:

    • scp
    • -p
    • --
    • host1.com:foo\(s\) or host1.com:'foo(s)'
    • host2.com:foo\(s\) or host2.com:'foo(s)'

    You can do that using the multi-argument syntax of system plus an escaping function.

    use String::ShellQuote qw( shell_quote );
    
    my $source = $from_server . ":" . shell_quote("$from_path/$filename");
    my $target = $to_server   . ":" . shell_quote("$to_path/$filename");
    
    system('scp', '-p', '--', $source, $target);
    

    If you really wanted to build a shell command, use shell_quote as usual.

    my $cmd = shell_quote('scp', '-p', '--', $source, $target);
    

    $ ssh ikegami@host.com 'mkdir foo ; touch foo/a foo/b foo/"*" ; ls -1 foo'
    *
    a
    b
    
    $ mkdir foo ; ls -1 foo
    
    $ scp 'ikegami@host.com:foo/*' foo
    *              100%    0     0.0KB/s   00:00
    a              100%    0     0.0KB/s   00:00
    b              100%    0     0.0KB/s   00:00
    
    $ ls -1 foo
    *
    a
    b
    
    $ rm foo/* ; ls -1 foo
    
    $ scp 'ikegami@host.com:foo/\*' foo
    *              100%    0     0.0KB/s   00:00
    
    $ ls -1 foo
    *