Search code examples
pythonstringsubprocessrsync

Python: split and format space separated values in a string to use in subprocess.check_call


I am using subprocess.check_call in combination with rsync.

I need to use arguments for rsync that are coming from a string which contains multiple space separated values, however because the string is a single object it fails in the subprocess.check_call (which expects each argument to be a separate string).

This is what I'm talking about:

import subprocess
rsync_options = '-axh --delete --delete-excluded'
subprocess.check_call(['rsync', rsync_options, '/tmp/1', '/tmp/2'])

This returns the following expection:

subprocess.CalledProcessError: Command '['rsync', '-axh --delete --delete-excluded', '/tmp/1', '/tmp/2']' returned non-zero exit status 1

This works:

subprocess.check_call(['rsync', '-axh', '--delete', '--delete-excluded', '/tmp/1', '/tmp/2'])

How can I generate seperate strings from rsync_options and format them for use in subprocess.check_call also without knowing how many arguments might be provided?


Solution

  • If I understand your question correctly, you can use shlex.split. (As JAB points out, rsync_options.split() is also an option in this particular case, but it fails in certain corner cases, as illustrated by the note here.)

    >>> import shlex
    >>> shlex.split('-axh --delete --delete-excluded')
    ['-axh', '--delete', '--delete-excluded']
    

    Then you can insert, append, or extend the result in any way you like.


    Perhaps the simplest way to construct the final list is concatenation:

    >>> rsync_options = '-axh --delete --delete-excluded'
    >>> rsync_args = ['rsync'] + shlex.split(rsync_options) + ['/tmp/1', '/tmp/2']
    >>> rsync_args
    ['rsync', '-axh', '--delete', '--delete-excluded', '/tmp/1', '/tmp/2']
    

    But that makes a copy. That probably won't matter in this case, but just for the sake of completeness, here's a way to do it without making a copy:

    >>> rsync_args = shlex.split(rsync_options)
    >>> rsync_args.extend(['/tmp/1', '/tmp/2'])
    >>> rsync_args.insert(0, 'rsync')
    >>> rsync_args
    ['rsync', '-axh', '--delete', '--delete-excluded', '/tmp/1', '/tmp/2']