BASH : automatic quoting of tilde (~) character

那年仲夏 提交于 2021-02-05 10:47:25

问题


Here is a small snippet to showcase the problem

#/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."

The output is

[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.

As we can see, the actual command that bash tries to run is

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

What I don't understand is why should the ~ (and the surrounding parameters) be quoted? Bash does it, but how to stop it?

Well, I got the script running good by using .bak suffix and ${HOME}. Still I am curious...

Thanks


回答1:


The shell does various sorts of expansions (~, wildcard expansions, variable substitutions, etc) in somewhat unpredictable order. For ~, it'll get replaced with your home path when used (unquoted!) in a variable assignment, but not when the variable is used:

$ 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

...so one possibility is to use $HOME instead (which will get expanded inside double-quotes. Another is to leave the ~ part of the variable assignment unquoted (like SRCDIR_DOC=~om/"${DOCDIR}/"). (Actually, SRCDIR_DOC=~om/${DOCDIR}/ would also be safe since the usual problems with unquoted variable references don't apply to assignments -- but I prefer to double-quote all variable references instead of trying to remember where it's safe and where it isn't.)

But I'd also recommend storing the options as an array rather than a plain string variable. This makes this and a number of other potential problems pretty much go away (see BashFAQ #50: I'm trying to put a command in a variable, but the complex cases always fail!). The syntax is a bit more complex, but it's really the more robust way to do this sort of thing. Also, double-quoting variable references is almost always a good idea. Finally, I'd also recommend using lowercase variable names, to prevent accidental conflicts with the various special variables that the shell and other programs use (the classic example is assigning to PATH, then discovering that the shell can't find any executables anymore). Something like this:

#/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."


来源:https://stackoverflow.com/questions/40076862/bash-automatic-quoting-of-tilde-character

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!