bash 用于存储带空格的目录名称的变量

bash variable to store directory names with spaces

我正在编写一个 bash 脚本来收集一些目录(满足条件的目录)和 rsync 那些到远程位置的目录。总体而言,脚本如下所示;

sources=""
for d in /somewhere/* ; do 
  if $d meets condition; then
    sources="$sources $(printf %q "$d")"
  fi
done
if [ ! -z $sources ] ; then 
  rsync -vrz $sources /remote/target/
fi

请注意,我使用 printf %q 来转义目录名称中的空格。 但是当目录名中有空格时,例如"/somewhere/dir name"满足条件时,rsync认为是两个目录而无法运行;

(at /home/u/) $ bash script.sh

sending incremental file list
rsync: link_stat "/somewhere/dir\" failed: No such file or directory (2)
rsync: link_stat "/home/u/name" failed: No such file or directory (2)

rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1196) [sender=3.1.2]

如果我只是通过将最后一行更改为

来打印 rsync 命令
echo rsync -vrz $sources /remote/target/

看起来还不错。

(at /home/u/) $ bash script.sh
rsync -vrz /somewhere/dirname /somewhere/dir\ name /remote/target

但是使用 set -x 表明发生了一些古怪的事情。

(at /home/u/) $ bash script.sh
+ rsync -vrz /somewhere/dirname '/somewhere/dir\' name /remote/target
sending incremental file list
rsync: link_stat "/somewhere/dir\" failed: No such file or directory (2)
rsync: link_stat "/home/u/name" failed: No such file or directory (2)

rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1196) [sender=3.1.2]

我也尝试使用双引号目录名而不是 printf %q 但它也没有用,原因略有不同。

(at /home/u/) $ bash script.sh
+ rsync -vrz '"/somewhere/dirname"' '"/somewhere/dir' 'name"' /remote/target
sending incremental file list
rsync: change_dir "/home/u//"/somewhere" failed: No such file or directory (2)
rsync: change_dir "/home/u//"/somewhere" failed: No such file or directory (2)
rsync: link_stat "/home/u/name"" failed: No such file or directory (2)

sent 20 bytes  received 12 bytes  64.00 bytes/sec
total size is 0  speedup is 0.00
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1196) [sender=3.1.2]

一些参数周围的单引号来自哪里?在单行变量中收集带空格的目录以用作 cpmvmv 中的源的最佳方法是什么rsync?

改用数组。

sources=()
for d in /somewhere/* ; do 
  if $d meets condition; then
    sources+=("$d")
  fi
done
if [ "${sources[*]}" ]; then 
  rsync -vrz "${sources[@]}" /remote/target/
fi

使用 set -x 输出的单引号只是为了消除歧义,因此您可以准确地看到实际文字空间的位置(与作为参数分隔符的语法空间相反)。