繁体   English   中英

bash tar错误不会创建tar.gz

[英]bash tar error doesn't create tar.gz

我有以下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脚本有什么问题?

如果你执行字符串$ cmd,如果“filename”嵌入空格则不起作用你必须让bash创建参数。 像这样:

tar czvf“$ {filename}”$ subdirs

你甚至不需要在文件名中加上'\\'

您可以通过不同的方式获得结果,而不是沉溺于混乱的引用问题,或许可以节省一些时间。 这样的事怎么样?

#!/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}"

我没有道歉,但在现实世界中请注意,你想要制作正确的临时文件。

简短的回答:请参阅BashFAQ#050:我正在尝试将命令放入变量中,但复杂的情况总是会失败!

答案很长:在变量中嵌入引号并没有做任何有用的事情,因为当你使用它时(即$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[@]}" 

好的,原始脚本不起作用,因为文件/路径确定变量扩展之前发生,因此文件名错误: 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

我建议你从$ filename和$ subdirs中分离$ cmd。 我认为诱导错误来自于你加入这些字符串时。 此外,在没有适当引用的情况下在一个变量中使用多个变量也会导致错误。

这应该适合你:

cmd="tar -zcvf"
subdirs="etc/ sbin/ bin/"
filename="${DIR}/Backup_${backup_name}_${date}.tar.gz"
$cmd $filename $subdirs
#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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM