0

I need to quote part of the rsync line that subprocess.run uses that contains the ssh parameters, unfortunately nothing I have tried has worked so far.

Can someone please advise me on the correct way to quote the ssh parameters, so that it will run under rsync.

At first I had a list of lists that got passed to subprocess.run, that fails with:

Traceback (most recent call last):
  File "./tmp.py", line 20, in <module>
    process = subprocess.run(rsync_cmd, stderr=subprocess.PIPE)
  File "/usr/lib/python3.6/subprocess.py", line 423, in run
    with Popen(*popenargs, **kwargs) as process:
  File "/usr/lib/python3.6/subprocess.py", line 729, in __init__
    restore_signals, start_new_session)
  File "/usr/lib/python3.6/subprocess.py", line 1295, in _execute_child
    restore_signals, start_new_session, preexec_fn)
TypeError: expected str, bytes or os.PathLike object, not list

Flatten it to an ordinary list:

Unexpected remote arg: example.com:/var/log/maillog
rsync error: syntax or usage error (code 1) at main.c(1361) [sender=3.1.2]

Which makes sense, as part of the command line for rsync needs to be quoted.

So I try to quote it:

rsync: Failed to exec /usr/bin/ssh -F /home/rspencer/.ssh/config -o PreferredAuthentications=publickey -o StrictHostKeyChecking=accept-new -o TCPKeepAlive=yes -o ServerAliveInterval=5 -o ServerAliveCountMax=24 -o ConnectTimeout=30 -o ExitOnForwardFailure=yes -o ControlMaster=autoask -o ControlPath=/run/user/1000/foo-ssh-master-%C -l root -p 234 -o Compression=yes: No such file or directory (2)
rsync error: error in IPC code (code 14) at pipe.c(85) [Receiver=3.1.2]
rsync: connection unexpectedly closed (0 bytes received so far) [Receiver]
rsync error: error in IPC code (code 14) at io.c(235) [Receiver=3.1.2]

Which is due, I expect, to it being a string instead of a list. Although I'm guessing and that does not make complete sense to me.

Summarized code of my last attempt:

#!/usr/bin/python3

import subprocess

ssh_args = [
    "-F",
    "/home/rspencer/.ssh/config",
    "-o",
    "PreferredAuthentications=publickey",
    "-o",
    "StrictHostKeyChecking=accept-new",
    "-o",
    "TCPKeepAlive=yes",
    "-o",
    "ServerAliveInterval=5",
    "-o",
    "ServerAliveCountMax=24",
    "-o",
    "ConnectTimeout=30",
    "-o",
    "ExitOnForwardFailure=yes",
    "-o",
    "ControlMaster=autoask",
    "-o",
    "ControlPath=/run/user/1000/foo-ssh-master-%C",
    "-l",
    "root",
    "-p",
    "234",
]
rsync_params = []
src = "example.com:/var/log/maillog"
dest = "."

# Build SSH command
ssh_cmd = ["/usr/bin/ssh"] + ssh_args

# Use basic compression
ssh_cmd.extend(["-o", "Compression=yes"])

ssh_cmd = " ".join(ssh_cmd)
ssh_cmd = f'"{ssh_cmd}"'

# Build rsync command
rsync_cmd = ["/usr/bin/rsync", "-vP", "-e", ssh_cmd] + rsync_params + [src, dest]

# Run rsync
process = subprocess.run(rsync_cmd, stderr=subprocess.PIPE)

if process.returncode != 0:
    print(process.stderr.decode("UTF-8").strip())

What the correct command would look like on the command line:

/usr/bin/rsync -vP -e "/usr/bin/ssh -F /home/rspencer/.ssh/config -o \
PreferredAuthentications=publickey -o StrictHostKeyChecking=accept-new -o \
TCPKeepAlive=yes -o ServerAliveInterval=5 -o ServerAliveCountMax=24 -o \
ConnectTimeout=30 -o ExitOnForwardFailure=yes -o ControlMaster=autoask \
-o ControlPath=/run/user/1000/foo-ssh-master-%C -l root -p 234 -o \
Compression=yes" example.com:/var/log/maillog .

1 Answers1

0

Turns out the trick is to not try to quote it.

I removed the following line and it worked without further modification:

ssh_cmd = f'"{ssh_cmd}"'

I've read so much documentation and missed it until asking the question. Murphy.

Rereading the post "How not to quote argument in subprocess?" and finally understanding what Greg Hewgill was saying helped me. I blame lack of sleep.

"If you use quotes on the shell command line, then put the whole contents in one element of args (without the quotes). ..." - Greg Hewgill