2

Somebody came up with the brilliant idea of putting spaces in a filename. I need to do scp from python using that filename, which is problematic because the shell parses the command, and scp has also some quircks regarding spaces. This is my test code:

import subprocess import shlex def split_it(command): return shlex.split(command) #return command.split(" ") def upload_file(localfile, host, mypath): command = split_it('scp {} {}:"{}"'.format(localfile, host, mypath)) print(command) res = subprocess.run(command, stdout=subprocess.PIPE) return res.stdout.decode() upload_file("localfile.txt", "hostname", "/some/directory/a file with spaces.txt") 

Which gives:

['scp', 'localfile.txt', 'hostname:/some/directory/a file with spaces.txt'] scp: ambiguous target 

Using the naive version with command.split(" "):

['scp', 'localfile.txt', 'hostname:"/some/directory/a', 'file', 'with', 'spaces.txt"'] spaces.txt": No such file or directory 

The right, working scp command would be:

['scp', 'localfile.txt', 'hostname:"/some/directory/a file with spaces.txt"'] 
  1. Is there a ready solution for this?
  2. If not, what would be the robust way of doing:
split_it('scp localfile.txt hostname:"/some/directory/a file with spaces.txt"') # returns ['scp', 'localfile.txt', 'hostname:"/some/directory/a file with spaces.txt"'] 
29
  • Related, although not directly a duplicate: stackoverflow.com/questions/19858176/… Commented Jun 16, 2017 at 19:08
  • @mkrieger1: the quoting in your link is precisely the way I create the scp command at the start. The trouble starts after that. Commented Jun 16, 2017 at 19:09
  • Yes, as stated in the top answer there, you need to somehow double-escape the spaces. But I'm not sure if there's a good way to do that in Python. Commented Jun 16, 2017 at 19:10
  • 1
    i.e., command = ['scp', localfile, '{}:{}'.format(host, shlex.quote(mypath))] Commented Jun 16, 2017 at 19:22
  • 1
    @delavnog, any parameter is, eventually, passed to the program you're running as an array element (remember, int main(int argc, char** argv) is part of calling convention for any case when you're calling a new executable, no matter what language it's implemented in). Why are you trying to create a string in such a format as to be correctly split into an array, instead of just specifying the array of strings you actually want in the first place? Commented Jun 16, 2017 at 19:28

1 Answer 1

4
command = split_it('scp {} {}:"{}"'.format(localfile, host, mypath)) 
  1. Instead of building a command string, only to split_it again, directly build a list of arguments.

  2. In order to add one layer of quoting to the remote file path, use shlex.quote (or pipes.quote if using older Python versions).

command = ['scp', localfile, '{}:{}'.format(host, shlex.quote(mypath))] 

Sources/related posts:

Sign up to request clarification or add additional context in comments.

2 Comments

This doesnt appear to work on windows, it adds a single quote when I need a double quote to allow windows to intepret spaces etc. If I hard code a double quote then Popen covnerts it to \" and my scripts get confused
Sounds like you should ask a new question with a minimal example reproducing your problem.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.