将变量设置为 ~/directory 不会扩展

将变量设置为 ~/directory 不会扩展

我正在尝试使用 tar 设置一个简单的备份脚本,如下所示:

#!/bin/bash
TIME=`date +%F_%H-%M`
FILENAME=backup-$TIME.tar.gz
SRCDIR="~/PI/tutos/* ~/scripts/*"
DESTDIR=/media/sf_ubuntuSharedFolder/backups
tar cvpzf $FILENAME $SRCDIR

但是,使用 $SRCDIR 会导致 tar 错误,而如果我自己扩展它,则不会出现该错误。

alex@ALeX-VirtualBox:~$ save
tar: ~/PI/tutos/*: Cannot stat: No such file or directory
tar: ~/scripts/*: Cannot stat: No such file or directory

使用 shell 变量有什么问题?否则,如何在一行中将文件夹列表传递到 tar ?

答案1

问题是~-expansion 不会在变量扩展时执行(当您引用$SRCDIR未加引号的变量时),也不会在双引号内执行(当您分配该SRCDIR变量时)。

如果$SRCDIR不加引号,您将调用 split+glob 运算符。也就是说,存储在标量$SRCDIR变量 ( ~/PI/tutos/* ~/scripts/*) 中的字符串首先根据$IFS(默认为空白)进行分割,然后每个单词都进行通配,即被视为扩展到匹配文件列表的模式。

因为~它没有扩展到您的主目录,所以它~只是像任何其他字符一样对待,所以它只是在目录中查找文件~/PI/tutos,其中~将是当前目录中的目录,在您的情况下该目录不存在。

最好的办法是创建$SRCDIR一个数组并在赋值时扩展 glob:

SRCDIR=(~/PI/tutos/* ~/scripts/*) # ~ and globs expanded at this point
tar cvpzf "$FILENAME" "${SRCDIR[@]}"

请注意,应用 split+glob 运算$FILENAME符没有意义,因此我们通过引用 来禁用它$FILENAME

请注意,如果~/PI/tutos/*不匹配,它将保持原样,因此您仍然会收到错误tar。为了避免这种情况,你可以这样做:

shopt -s nullglob # remove non-matching globs
SRCDIR=(~/PI/tutos/* ~/scripts/*)
if ((${#SRCDIR[@]} != 0)); then
  tar cvpzf "$FILENAME" "${SRCDIR[@]}"
fi

您可能会想做:

SRCDIR="$HOME/PI/tutos/* $HOME/scripts/*"
tar cvpzf "$FILENAME" $SRCDIR

由于变量(例如$HOME)在双引号内扩展,但我建议不要这样做,因为如果$HOME包含全局字符或$IFS.

~当未加引号以及位于开头或后面时, s 在变量赋值中展开:(这样它就可以在诸如$PATH, $LD_LIBRARY_PATH... 之类的变量赋值中工作PATH=~/bin:~/sbin)。所以你可能会想做:

SRCDIR=~/PI/tutos/*:~/scripts/* # ~ (not globs) expanded here
IFS=:
tar cvpzf "$FILENAME" $SRCDIR

但这与上面相同,如果$HOME包含$IFS字符(这次:,不太可能,因为/etc/passwd它定义您的主目录是冒号分隔的表)或全局字符,则无法正常工作。

相关内容