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

AAL*_*eXX 6 tar shell-script

我正在尝试使用 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
Run Code Online (Sandbox Code Playgroud)

但是,$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
Run Code Online (Sandbox Code Playgroud)

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

Sté*_*las 10

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

在不加$SRCDIR引号的情况下,您正在调用 split+glob 运算符。即存储在该标量$SRCDIR变量 ( ~/PI/tutos/* ~/scripts/*) 中的字符串首先根据$IFS(默认为空白)进行拆分,然后每个单词都经过 globbing,即被视为扩展到匹配文件列表的模式。

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

最好的方法是创建$SRCDIR一个数组并在分配时扩展 globs:

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

请注意,在上应用 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
Run Code Online (Sandbox Code Playgroud)

您可能会尝试这样做:

SRCDIR="$HOME/PI/tutos/* $HOME/scripts/*"
tar cvpzf "$FILENAME" $SRCDIR
Run Code Online (Sandbox Code Playgroud)

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

~s 在未引用时以及在开头或后面时在变量赋值中扩展:(因此它可以在变量赋值中工作,例如$PATH, $LD_LIBRARY_PATH... 例如PATH=~/bin:~/sbin)。因此,您可能会尝试这样做:

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

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