BASH : 自动引用波浪号 (~) 字符

BASH : automatic quoting of tilde (~) character

这里是一个小片段来展示问题

#/usr/bin/bash

RSYNC=/usr/bin/rsync
RSYNC_OPTIONS="-aq --backup --suffix=~ --backup=bkpdir --update"

echo ${RSYNC_OPTIONS}

DOCDIR="Documents" # Note : no trailing slash
BKPDIR="Active-Backups"
HOST=$(hostname)

SRCDIR_DOC="~om/${DOCDIR}/" # Trailing slash added

DESTDIR=$(readlink -f $(dirname "[=11=]") )/${BKPDIR}/${HOST}
echo "DESTDIR = ${DESTDIR}"

echo "Backing up ${SRCDIR_DOC}"
echo "${RSYNC} ${RSYNC_OPTIONS} ${SRCDIR_DOC} ${DESTDIR}/${DOCDIR}"
set -x 
${RSYNC} ${RSYNC_OPTIONS} ${SRCDIR_DOC} ${DESTDIR}/${DOCDIR}
set +x

echo "Backup complete."

输出为

[1329]$ bash generic-doc-dow-backup.sh 
-aq --backup --suffix=~ --backup=bkpdir --update
DESTDIR = /run/media/om/seagate1/Active-Backups/e6431
+ echo 'Backing up ~om/Documents/'
Backing up ~om/Documents/
+ echo '/usr/bin/rsync -aq --backup --suffix=~ --backup=bkpdir --update ~om/Documents/ /run/media/om/seagate1/Active-Backups/e6431/Documents'
/usr/bin/rsync -aq --backup --suffix=~ --backup=bkpdir --update ~om/Documents/ /run/media/om/seagate1/Active-Backups/e6431/Documents
+ /usr/bin/rsync -aq --backup '--suffix=~' --backup=bkpdir --update '~om/Documents/' /run/media/om/seagate1/Active-Backups/e6431/Documents
rsync: change_dir "/run/media/om/seagate1//~om/Documents" failed: No such file or directory (2)
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1052) [sender=3.0.9]
+ set +x
Backup complete.

正如我们所见,bash 尝试 运行 的实际命令是

/usr/bin/rsync -aq --backup '--suffix=~' --backup=bkpdir --update '~om/Documents/' /run/media/om/seagate1/Active-Backups/e6431/Documents

我不明白的是为什么要引用 ~ (和周围的参数)? Bash 会,但如何停止?

好吧,我通过使用 .bak 后缀和 ${HOME} 获得了脚本 运行ning。我还是很好奇...

谢谢

shell 以某种不可预测的顺序进行各种扩展(~、通配符扩展、变量替换等)。对于 ~,在变量赋值中使用(不加引号!)时,它会被您的主路径替换,但在使用变量时不会:

$ var="~"    # With quotes, the ~ will be left alone
$ echo "$var"
~
$ echo "$var"    # Referencing it without quotes, it still won't be expanded
~
$ var=~    # Without quotes in the assignment, it WILL be expanded
$ echo "$var"
/Users/gordon

...所以一种可能性是使用 $HOME 代替(这将在双引号内展开。另一种方法是将变量赋值的 ~ 部分不加引号(如 SRCDIR_DOC=~om/"${DOCDIR}/").(实际上,SRCDIR_DOC=~om/${DOCDIR}/ 也是安全的,因为不带引号的变量引用的常见问题不适用于赋值——但我更喜欢双引号所有变量引用,而不是试图记住它在哪里安全和不安全的地方。)

但我还建议将选项存储为数组而不是纯字符串变量。这使得这个问题和许多其他潜在问题几乎消失了(参见 BashFAQ #50: I'm trying to put a command in a variable, but the complex cases always fail!)。语法有点复杂,但它确实是执行此类操作的更可靠的方法。此外,双引号变量引用几乎总是一个好主意。最后,我还建议使用小写变量名,以防止与 shell 和其他程序使用的各种特殊变量发生意外冲突(经典示例是分配给 PATH,然后发现 shell找不到任何可执行文件了)。像这样:

#/usr/bin/bash

rsync=/usr/bin/rsync
rsync_options=(-aq --backup --suffix=~ --backup=bkpdir --update)

# printf "%q" will print values with any necessary quoting (but without a
# linefeed at the end, so add an echo afterward); "${arr[@]}" is the standard
# idiom for getting all elements of an array, without word-splitting or
# wildcard expansion.
printf "%q " "${rsync_options[@]}"
echo

docdir="Documents" # Note : no trailing slash
bkpdir="Active-Backups"
host="$(hostname)"

srcdir_doc=~om/"${docdir}/" # Trailing slash added

destdir="$(readlink -f "$(dirname "[=11=]")" )/${bkpdir}/${host}"
echo "destdir = ${destdir}"

echo "Backing up ${srcdir_doc}"
printf "%q " "${rsync}" "${rsync_options[@]}" "${srcdir_doc}" "${destdir}/${docdir}"
echo
set -x 
"${rsync}" "${rsync_options[@]}" "${srcdir_doc}" "${destdir}/${docdir}"
set +x

echo "Backup complete."