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

时间:2016-10-17 00:09:24

标签: bash tilde

这是一个展示问题的小片段

#/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 "$0") )/${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}让脚本运行良好。我还好奇......

由于

1 个答案:

答案 0 :(得分:1)

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}/也是安全的,因为不带引号的变量引用的常见问题不适用于赋值 - 但我更喜欢引用所有变量引用而不是试图记住它的位置#39;安全且不在哪里。)

但我还建议将选项存储为数组而不是普通的字符串变量。这使得这个和其他一些潜在的问题几乎消失了(见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 "$0")" )/${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."