bash tar错误不会创建tar.gz

时间:2012-03-22 15:09:50

标签: linux bash tar

我有以下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
  • 这两个变量:从文件中读取backup_name和subdirs(我没有在代码中包含读取过程)

编辑:我刚刚将我的脚本复制到没有空格的目录并更改了该行:

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脚本出了什么问题?

6 个答案:

答案 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在替换变量之前解析引号;当报价出现时,他们做任何好事都为时已晚。但是,您有几种选择:

  1. 首先不要把命令放在变量中,只需直接使用它:

    echo "filename: $filename"
    echo "subdirs $subdirs"
    tar czvf "$filename" $subdirs
    
  2. 如果你真的需要先将它放在变量中,请使用数组而不是纯文本变量(理想情况下,对子目录列表执行相同操作):

    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