【问题标题】:bash tar error doesn't create tar.gzbash tar 错误不会创建 tar.gz
【发布时间】:2012-03-22 15:09:49
【问题描述】:

我有以下 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"

它现在正在工作,但是当我在一个也包含空格的目录中执行相同操作时,我仍然得到相同的错误。

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

【问题讨论】:

  • f 应该是最后一个选项,因为它需要您的 tar 文件参数。试试tar czvf
  • 还是一样,但谢谢提供信息。
  • 请编辑您的帖子以包含您的$config 文件中的问题行。祝你好运。

标签: linux bash tar


【解决方案1】:

简答:见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[@]}"
    

【讨论】:

    【解决方案2】:

    您可以通过不同的方式获得您想要的结果,而不是纠结于凌乱的引用问题,并且也许可以节省一些时间。这样的事情怎么样?

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

    我对此一无所知,但在现实世界中请注意,您需要制作适当的临时文件。

    【讨论】:

      【解决方案3】:

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

      tar czvf "${filename}" $subdirs

      你甚至不需要把'\'放在文件名中

      【讨论】:

      • thx,“${filename}”和“$filename”变量有什么区别?
      • "$filename" 是 "${filename}" 的一个特例,在这个特定场景中是一样的,更通用的 ${...} 允许进一步的替换和其他微妙之处。跨度>
      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        我建议您将 $cmd 与 $filename 和 $subdirs 分开。我认为引发的错误来自于您加入这些字符串时。此外,在一个变量中使用多个变量而没有正确引用也会导致错误。

        这应该适合你:

        cmd="tar -zcvf"
        subdirs="etc/ sbin/ bin/"
        filename="${DIR}/Backup_${backup_name}_${date}.tar.gz"
        $cmd $filename $subdirs
        

        【讨论】:

          【解决方案6】:
          #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
          

          【讨论】:

          • 我没有引用 subdirs 和 backup_name 因为我是从文件中读取的。但是,即使我像您建议的那样引用 subdirs="etc/sbin/bin/" 也会被视为一个不存在的目录。
          • @jack 请附上您如何从文件中读取子目录。引用它对我有用。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-22
          • 2014-01-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多