展开一个变量,该变量的值包含一个选项和一个带空格的路径名

展开一个变量,该变量的值包含一个选项和一个带空格的路径名
$ md="-l /tmp/test/my dir"
$ ls "$md"
ls: invalid option -- ' '
Try 'ls --help' for more information.

$ md="-l \"/tmp/test/my dir\""
$ ls "$md"
ls: invalid option -- ' '
Try 'ls --help' for more information.

我想知道为什么两者都不起作用?谢谢。请注意,变量的值包含

  • 一个选项-l
  • 包含空格的路径名参数
  • 一个空格将以上两者分开。

我的实际问题是:我写了一个脚本myscript.sh

#! /bin/bash

old_dest=""
while getopts ":l:t" opt; do
    case $opt in
        l)
            old_dest="--link-dest \"$OPTARG\""
            ;;
    esac
done

rsync -a --delete "$old_dest" /path/to/source  /path/to/dest

我想将脚本作为-l带有选项的命令来调用。

myscript.sh -l "/media/t/my external hdd/backup/old one"

我应该如何编写脚本中分配给 的部分old_dest

答案1

您的命令不起作用的原因是引号删除/单词拆分没有以递归方式应用。

因此"$old_dest"扩展为以下 SINGLE 参数:

--link-dest "/media/t/my external hdd/backup/old one"

所有这些都是一个参数,因为变量用双引号引起来。 (那些双引号被删除,但是双引号扩张的结果,即变量值的一部分,不会被删除。)

如果你使用$old_dest相反的方式,你会得到多个参数,但不是你想要的方式。你会得到以下五个参数:

--link-dest
"/media/t/my
external
hdd/backup/old
one"

这是因为您将获得变量扩展,然后进行分词,如手册中所述。你会不是获取变量扩展,然后解释变量中的引号。并且引号不会被删除,因此最终参数将是四个字符:one"

正如另一个答案所述,正确的方法是使用 Bash 数组。


另外,您可能会喜欢 Tcl 处理引号和分词的方式。它可能更直观,尽管我对 shell 分词非常熟悉,但我必须更多地考虑 Tcl 而不是 shell。

答案2

由于您使用的是 bash,并且 bash 支持数组,因此我建议将 old_dest 设为数组:

#! /bin/bash

old_dest=()
while getopts ":l:t" opt; do
    case $opt in
        l)
            old_dest+=('--link-dest' "$OPTARG")
            ;;
    esac
done

printf 'Element: %s\n' rsync -a --delete "${old_dest[@]}" /path/to/source  /path/to/dest

我将对 rsync 的调用改为对 printf 的调用,以在单独的行上显示命令和每个参数。执行时:

./myscript.sh -l "/media/t/my external hdd/backup/old one"

结果是:

Element: rsync
Element: -a
Element: --delete
Element: --link-dest
Element: /media/t/my external hdd/backup/old one
Element: /path/to/source
Element: /path/to/dest

相关内容