【问题标题】:BASH : automatic quoting of tilde (~) characterBASH : 波浪号 (~) 字符的自动引用
【发布时间】:2016-10-17 00:09:24
【问题描述】:

这是一个小sn-p来展示问题

#/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} 使脚本运行良好。我还是很好奇...

谢谢

【问题讨论】:

    标签: bash tilde


    【解决方案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}/ 也很安全,因为不带引号的变量引用的常见问题不适用于赋值——但我更喜欢双引号引用所有变量引用,而不是试图记住它在哪里是安全的,在哪里不安全。)

    但我也建议将选项存储为数组而不是纯字符串变量。这使得这个和许多其他潜在问题几乎消失了(参见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."
    

    【讨论】:

    • 虽然您的建议是一个很好的答案,但我的原始问题仍未解决。让我澄清如下
    • 抱歉,我按 太快了
    • #!/usr/bin/bash -x #这是一个sn -p #文件名:expansion-tilde-test.sh set -x var="~om" echoing echoing "$var " 作为参数 var="~om" 回显 "$var" 作为参数 var=~om echo "$var" set +x $ bash ./expansion-tilde-test.sh + var='~om' + echo echoing '~om' 作为参数 # 请注意单引号。谁把它放在那里的? echoing ~om 作为参数 + var='~om' + echo echoing '~om' 作为参数 # 请注意单引号。为什么不是双引号?谁把它放在那里的?回显 ~om 作为参数 + var=/home/om + echo /home/om /home/om + set +x
    • @OmNarasimhan:我在分配给 srcdir_doc 时犯了一个错误——引号中的 ~om/ 部分没有扩展到您的主路径。但请注意,其余部分可能应该被引用,因此正确的分配是srcdir_doc=~om/"${docdir}/"。至于您看到的单引号,set -x 模式会打印一个 equivalent 命令行来执行正在执行的操作——var 设置为“~om”,命令echo "$var"echo $varecho "~om"echo '~om'echo \~om 都是等价的,所以 set -x 可以打印它们中的任何一个——它恰好选择了 echo '~om'
    猜你喜欢
    • 2011-06-06
    • 2012-12-04
    • 2014-07-15
    • 2013-03-29
    • 1970-01-01
    • 2022-11-19
    • 1970-01-01
    • 2010-10-24
    相关资源
    最近更新 更多