Shell: rsync 错误地解析文件中的空格 name/path

Shell: rsync parsing spaces incorrectly in file name/path

我正在尝试使用 rsync 通过 ssh 拉取文件列表,但我无法让它处理带有空格的文件名!一个示例文件是这样的:

/home/pi/Transmission_Downloads/FUNDAMENTOS_JAVA_E_ORIENTAÇÃO_A_OBJETOS/2. Fundamentos da linguagem/estruturas-de-controle-if-else-if-e-else-v1.mp4

我正在尝试使用此 shell 代码传输它。

cat $file_name | while read LINE
do
    echo $LINE
    rsync -aP "$user@$server:$LINE" $local_folder
done

我得到的错误是:

receiving incremental file list
rsync: link_stat "/home/pi/Transmission_Downloads/FUNDAMENTOS_JAVA_E_ORIENTAÇÃO_A_OBJETOS/2." failed: No such file or directory (2)
rsync: link_stat "/home/pi/Fundamentos" failed: No such file or directory (2)
rsync: link_stat "/home/pi/da" failed: No such file or directory (2)
rsync: change_dir "/home/pi//linguagem" failed: No such file or directory (2)
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1655) [Receiver=3.1.0]

我不明白为什么它在屏幕上打印正常,但解析文件 name/path 不正确!我知道空格实际上是带空格的反斜杠,但不知道如何解决这个问题。 Sed (find/replace) 也没有帮助,我也尝试了这段代码但没有成功

while IFS='' read -r line || [[ -n "$line" ]]; do
    echo "Text read from file: $line"
    rsync -aP "$user@$server:$line" $local_folder
done < $file_name

我应该怎么做才能解决这个问题,为什么会这样?

我从 .txt 文件中读取文件列表(每个文件和路径在一行中),并且我使用的是 ubuntu 14.04。谢谢!

shell 正确地将文件名传递给 rsync,但 rsync 将空格解释为分隔同一服务器上的多个路径。因此,除了双引号变量扩展以确保 rsync 将字符串视为单个参数外,您 需要引用文件名中的空格。

如果您的文件名中没有撇号,您可以在双引号内使用单引号:

rsync -aP "$user@$server:'$LINE'" "$local_folder"

如果您的文件名中可能有撇号,那么您需要引用它们(无论文件名是否也有空格)。您可以使用 bash 的内置参数替换来执行此操作(只要您使用的是 bash 4;旧版本,例如 [=69 上提供的 /bin/bash =] X,此类表达式中的反斜杠和撇号有问题)。这是它的样子:

rsync -aP "$user@$server:'${LINE//\'/\'\\'\'}'" "$local_folder"

丑陋,我知道,但有效。解释在其他选项之后。

如果您使用的是旧版 bash 或不同的 shell,您可以改用 sed

rsync -aP "$user@$server:'$(sed "s/'/'\\''/g" <<<"$LINE")'" "$local_folder"

... 或者如果您的 shell 也不支持 <<< 此处字符串:

rsync -aP "$user@$server:'$(echo "$LINE" | sed "s/'/'\\''/g")'" "$local_folder"

说明:我们想将所有撇号替换为.. 在单引号字符串中间变成文字撇号的东西。由于无法对单引号内的任何内容进行转义,因此我们必须先关闭引号,然后添加文字撇号,然后重新打开字符串其余部分的引号。实际上,这意味着我们要用序列(撇号、反斜杠、撇号、撇号)替换所有出现的撇号 ('):'\''。我们可以使用 bash 参数扩展或 sed.

在 bash 中,${varname/old/new} 扩展为变量 $varname 的值,其中第一次出现的字符串 old 被字符​​串 new 替换.将第一个斜杠 ( ${varname//old/new} ) 加倍会替换 all 次出现,而不仅仅是第一个。这就是我们想要的。但是由于撇号和反斜杠对于 shell 都是特殊的,我们必须在两个表达式中的每个字符前面放置一个(另一个)反斜杠。这会将我们的 old 值变成 \',而我们的 new 变成 \'\\'\'

sed 版本稍微简单一些,因为撇号并不特殊。反斜杠仍然存在,因此我们必须在字符串中放入 \ 才能返回 \。由于我们希望在字符串中使用撇号,因此使用双引号字符串而不是单引号字符串更容易,但这意味着我们需要将所有反斜杠 再次 加倍以确保shell 将它们传给 sed 不受干扰。这就是 shell 命令具有 \\ 的原因:作为 \ 传递给 sed,它输出为 \.

rsync 默认进行 space 拆分。 您可以使用 -s(或 --protect-args)标志禁用它,或者您可以转义文件名

中的 spaces