我有以下bash脚本:
#DIR is something like: /home/foo/foobar/test/ without any whitespace but can also include whitespace
DIR="$( cd "$( dirname "$0" )" && pwd )"
#backup_name is read from a file
backup_name=FOOBAR
date=`date +%Y%m%d_%H%M_%S`
#subdirs is also read from the same file
subdirs=etc/ sbin/ bin/
filename="$DIR/Backup_$backup_name"_"$date.tar.gz"
cd /
echo "filename: $filename"
echo "subdirs $subdirs"
cmd='tar czvf "'$filename'" '$subdirs
echo "cmd tar: $cmd"
$cmd
但我得到以下输出:
filename: /home/foo/foobar/test/Backup_FOOBAR_20120322_1529_35.tar.gz
subdirs: etc/ sbin/ bin/
cmd tar: tar cfvz "/home/foo/foobar/test/Backup_FOOBAR_20120322_1529_35.tar.gz" etc/ sbin/ bin/
etc/
# ... list of files in etc
# but no files from sbin or bin directory
tar: "/home/foo/foobar/test/Backup_FOOBAR_20120322_1529_35.tar.gz": can open not execute: File or directory not found
tar: not recoverable error: abortion.
但是,当我复制tar命令的echo输出时,将cd转换为/并将其粘贴到它正在运行的bash shell中:
tar cfvz "/home/foo/foobar/test/Backup_FOOBAR_20120322_1529_35.tar.gz" etc/ sbin/ bin/
etc/
编辑:我刚刚将我的脚本复制到没有空格的目录并更改了该行:
cmd='tar czvf "'$filename'" '$subdirs
#to
cmd="tar czvf $filename $subdirs"
并且它现在正在工作但是当我在dir中执行相同操作时也会出现相同的错误。
edit2:从文件中读取(在其他任何事情发生之前读取文件)
config="config.txt"
local line
while read line
do
#points to next free element and declares it
config_lines[${#config_lines[@]}]=$line
done <$config
backup_name=${config_line[0]}
subdirs=${config_line[1]}
我的bash脚本出了什么问题?
答案 0 :(得分:3)
如果执行字符串$ cmd,如果“filename”嵌入空格,则无效 你必须让bash创建参数。 像这样:
tar czvf“$ {filename}”$ subdirs
你甚至不需要在文件名
中加上'\'答案 1 :(得分:3)
您可以通过不同的方式获得您想要的结果,并且可能节省一些时间,而不是乱糟糟的引用问题。这样的事情怎么样?
#!/usr/bin/env bash
# abusing set -v for fun and profit
tar_output=/tmp/$$.tarout
tar_command=/tmp/$$.tarcmd
tmp_script=/tmp/$$.script
dir="$(cd "$(dirname "$0")"; pwd)"
cat>"${tmp_script}"<<-'END'
datestamp=$(date +%Y%m%d_%H%M_%S)
subdirs=(etc sbin bin)
backup_name=FOOBAR
filename="$1/Backup_${backup_name}_${date}.tar.gz"
printf 'tar cmd: '
set -v
tar czvf "$filename" "${subdirs[@]}" 2>"$2"
set +v
END
bash "${tmp_script}" "$dir" "${tar_output}" 2>"${tar_command}"
cat "${tar_command}" | head -n 1 | sed -e 's/2>"\$2"$//'
cat "${tar_output}"
rm -f "${tmp_script}" "${tar_command}" "${tar_output}"
我为此道歉,但在现实世界中请注意,您需要制作正确的临时文件。
答案 2 :(得分:3)
简短回答:见BashFAQ #050: I'm trying to put a command in a variable, but the complex cases always fail!。
长答案:在变量中嵌入引号并没有做任何有用的事情,因为当你使用它时(即$cmd
),bash在替换变量之前解析引号;当报价出现时,他们做任何好事都为时已晚。但是,您有几种选择:
首先不要把命令放在变量中,只需直接使用它:
echo "filename: $filename"
echo "subdirs $subdirs"
tar czvf "$filename" $subdirs
如果你真的需要先将它放在变量中,请使用数组而不是纯文本变量(理想情况下,对子目录列表执行相同操作):
subdirs=(etc/ sbin/ bin/)
...
echo "filename: $filename"
echo "subdirs ${subdirs[*]}"
cmd=(tar czvf "$filename" "${subdirs[@]}")
printf "cmd tar:"
printf " %q" "${cmd[@]}" # Have to do some trickery to get it printed right
printf "\n"
"${cmd[@]}"
答案 3 :(得分:2)
好的,原始脚本无效,因为文件/路径确定在变量扩展之前发生,因此文件名错误:tar
认为它应该写入文件中的文件当前目录名为"/home/foo/foobar/test/Backup_FOOBAR_20120322_1529_35.tar.gz"
,即文件名包含斜杠和双引号!
tar cfz /this/file/does/nopt/exist .
tar: /this/file/does/nopt/exist: Cannot open: No such file or directory
tar: Error is not recoverable: exiting now
看到区别? tar
的错误消息中的文件名/路径周围没有双引号。
当您复制并粘贴该行时,它起作用,因为双引号由shell解释。
证人:
ls -l /tmp/screen-exchange
-rw-rw-rw- 1 aqn users 0 Mar 21 07:29 /tmp/screen-exchange
cmd='ls -l "'/tmp/screen-exchange'"'
$cmd
/bin/ls: "/tmp/screen-exchange": No such file or directory
eval $cmd
-rw-rw-rw- 1 aqn users 0 Mar 21 07:29 /tmp/screen-exchange
当然,使用eval
将无法防范其中包含空格的文件名。为防止这种情况,您的tar
命令必须如此:
date>'file name with spaces'
file='file name with spaces' # this is the equivalent of your $filename
cmd='ls -l "$file"'
$cmd
ls: "$file": No such file or directory
eval $cmd
-rw-r--r-- 1 andyn SPICE\Domain Users 1083 Mar 22 15:28 a b
答案 4 :(得分:1)
我建议你将$ cmd与$ filename和$ subdir分开。我认为诱导错误来自于你加入这些字符串时。此外,在没有正确引用的情况下在一个变量中使用多个变量也会导致错误。
这应该适合你:
cmd="tar -zcvf"
subdirs="etc/ sbin/ bin/"
filename="${DIR}/Backup_${backup_name}_${date}.tar.gz"
$cmd $filename $subdirs
答案 5 :(得分:0)
#DIR is something like: /home/foo/foobar/test/ without any whitespace but can also include whitespace
DIR="$( cd "$( dirname "$0" )" && pwd )"
backup_name=FOOBAR
date=`date +%Y%m%d_%H%M_%S`
subdirs="etc/ sbin/ bin/"
filename="$DIR/Backup_$backup_name"_"$date.tar.gz"
cd /
echo "filename: $filename"
echo "subdirs $subdirs"
cmd="tar zcvf $filename $subdirs"
echo "cmd tar: $cmd"
$cmd