If you know the remote system has a xargs
command that supports a -0
option, you can do:
printf '%s\0' "$DEST_PATH/subdir1" "$DEST_PATH/subdir2" |
ssh -i key 10.10.10.10 'xargs -0 mkdir -p --'
That xargs -0 mkdir -p --
piece of shell code would be interpreted the same by all shells (remember sshd
on the remote machine runs the login shell of the remote user to interpret the code given to ssh
which could be anything).
The list of arguments for mkdir
is passed NUL delimited via the remote shell's stdin, inherited by xargs
.
Another advantage with that approach is that you can pass any number of arguments. If needed xargs
will break that list and run several invocations of mkdir -p -- dirn dirn+1...
to bypass the limit of the size of the argument+environment list, even possibly starting the directory creation before the full list has been transferred for very large lists.
See How to execute an arbitrary simple command over ssh without knowing the login shell of the remote user? for other approaches.
rsync
itself by default has the same problem as it passes the name of the remote file/dir to sync from/to as part of that remote shell command line, and the better solution again is to take that remote shell out of the loop by using the -s
/--protect-args
which instead of passing the file/dir names in the shell code, passes it on the rsync
server's stdin.
So:
rsync -s -- "$SOURCE_PATH" "$DEST_HOST:$DEST_PATH/subdir1/"
Quoting the rsync
man page:
If you need to transfer a filename that contains whitespace, you can either specify the --protect-args
(-s
) option, or you’ll need to escape the whitespace in a way that the remote shell will understand. For instance:
rsync -av host:'file\ name\ with\ spaces' /dest
$ rsync --rsync-path='set -x; rsync' /etc/issue localhost:'1 2'
+zsh:1> rsync --server -e.LsfxC . 1 2
$ rsync --rsync-path='set -x; rsync' /etc/issue localhost:'1;uname>&2'
+zsh:1> rsync --server -e.LsfxC . 1
+zsh:1> uname
Linux
See how the file name is interpreted as shell code.
With -s
:
~$ rsync -s --rsync-path='set -x; rsync' /etc/issue localhost:'1;uname>&2'
+zsh:1> rsync --server -se.LsfxC
no trace of the file names on the remote shell command line.
~$ cat '1;uname>&2'
Ubuntu 20.04.1 LTS \n \l