我正在尝试使用 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
它定义您的主目录是冒号分隔的表)或全局字符,则无法正常工作。