您将如何优雅地处理此代码段以允许目录中有空格?

Joe*_*lav 7 bash ssh rsync shell-script quoting

我有一系列命令,例如:

ssh -i key 10.10.10.10 mkdir -p "${DEST_PATH}/subdir1" "${DEST_PATH}/subdir2"
rsync "${SOURCE_PATH}" "$DEST_HOST:${DEST_PATH}/subdir1"
...
Run Code Online (Sandbox Code Playgroud)

这些被馈送变量为

DEST_PATH=$( myfun "foo" )
SOURCE_PATH=$( myfun "bar" )
Run Code Online (Sandbox Code Playgroud)

这反过来又被馈送为字符串

function myfun {
  local VALUE=$( grep "$1" yadayada.txt | tail -1 | sed 's/someregex//' )
  echo "$VALUE"
}
Run Code Online (Sandbox Code Playgroud)

你会如何处理$VALUE有一些空间..就像如果$VALUEHello World?我以为总是使用""可以解决所有问题,但我错了。其他几个问题建议转义或使用单引号,但我不确定最好的方法。

谢谢

Sté*_*las 7

如果你知道远程系统有一个xargs支持-0选项的命令,你可以这样做:

printf '%s\0' "$DEST_PATH/subdir1" "$DEST_PATH/subdir2" |
  ssh -i key 10.10.10.10 'xargs -0 mkdir -p --'
Run Code Online (Sandbox Code Playgroud)

xargs -0 mkdir -p --一块的shell代码将由所有炮弹解释相同的(记得sshd在远程机器上运行的远程用户的登录shell来解释给的代码ssh可能是任何东西)。

的参数列表mkdir通过远程 shell 的标准输入以 NUL 分隔,由xargs.

这种方法的另一个优点是您可以传递任意数量的参数。如果需要,xargs将打破该列表并运行几次调用mkdir -p -- dirn dirn+1...以绕过参数+环境列表的大小限制,甚至可能在为非常大的列表传输完整列表之前开始创建目录。

请参阅如何在不知道远程用户的登录 shell 的情况下通过 ssh 执行任意简单命令?对于其他方法。

rsync默认情况下,它本身具有相同的问题,因为它将远程文件/目录的名称作为远程 shell 命令行的一部分传递给同步自/至同步,而更好的解决方案是使用-s/--protect-args它不是在 shell 代码中传递文件/目录名称,而是在rsync服务器的标准输入上传递它。

所以:

rsync -s -- "$SOURCE_PATH" "$DEST_HOST:$DEST_PATH/subdir1/"
Run Code Online (Sandbox Code Playgroud)

引用rsync手册页:

如果您需要传输包含空格的文件名,您可以指定--protect-args( -s) 选项,或者您需要以远程 shell 能够理解的方式转义空格。例如:

     rsync -av host:'file\ name\ with\ spaces' /dest
Run Code Online (Sandbox Code Playgroud)
$ 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
Run Code Online (Sandbox Code Playgroud)

查看如何将文件名解释为 shell 代码。

-s

~$ rsync -s --rsync-path='set -x; rsync' /etc/issue localhost:'1;uname>&2'
+zsh:1> rsync --server -se.LsfxC
Run Code Online (Sandbox Code Playgroud)

远程 shell 命令行上没有文件名的踪迹。

~$ cat '1;uname>&2'
Ubuntu 20.04.1 LTS \n \l
Run Code Online (Sandbox Code Playgroud)