Ошибка bash tar не создает tar.gz - PullRequest
2 голосов
/ 22 марта 2012

У меня есть следующий скрипт 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.

Однако, когда я копирую вывод echo команды tar, создаю cd/ и вставьте его в оболочку bash, в которой он работает:

tar cfvz "/home/foo/foobar/test/Backup_FOOBAR_20120322_1529_35.tar.gz" etc/ sbin/ bin/
etc/
  • Каждая переменная определена, и нет завершающего символа новой строки
  • Я также пробовал $ cmd с обратными чертами
  • две переменные: backup_name и subdirs читаются из файла (я не включил процесс чтения в код)

edit: Я только что скопировал свой скриптв каталог без пробелов и изменил строку:

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?

Ответы [ 6 ]

3 голосов
/ 23 марта 2012

Краткий ответ: см. 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[@]}"
    
3 голосов
/ 22 марта 2012

Вместо того, чтобы разбираться с беспорядочными проблемами цитирования, вы можете получить желаемые результаты другим способом и, возможно, сэкономить время.Как насчет этого?

#!/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 голосов
/ 22 марта 2012

Если вы выполните строку $ cmd, она не будет работать, если в «filename» будут вставлены пробелы. Вы должны разрешить bash создать аргументы.как это:

tar czvf "$ {filename}" $ subdirs

Вам даже не нужно ставить '\' в имени файла

2 голосов
/ 23 марта 2012

ОК, ваш оригинальный скрипт не работал, потому что определение файла / пути происходит до расширения переменной, поэтому имя файла неверно: 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 нет двойных кавычек вокруг имени файла / пути.

Сработало, когда вы скопировали и вставили строку, потому что тогда двойные кавычки интерпретируются оболочкой.

Свидетель:

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
1 голос
/ 22 марта 2012

Я бы посоветовал вам отделить $ cmd от $ filename и $ subdirs. Я думаю, что вызванная ошибка происходит, когда вы присоединяетесь к этим строкам. Кроме того, использование нескольких переменных в одной переменной без кавычек также приведет к ошибкам.

Это должно работать для вас:

cmd="tar -zcvf"
subdirs="etc/ sbin/ bin/"
filename="${DIR}/Backup_${backup_name}_${date}.tar.gz"
$cmd $filename $subdirs
0 голосов
/ 22 марта 2012
#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
...