Запустите в bash, строка как команда не работает (Duplicity)

Я использую Duplicity для резервного копирования моего сервера на другой.

Поскольку существует несколько серверов, я хочу создать скрипт bash, который запускается с cron делать разделение по папкам (и серверам). Для этого я сделал монтаж команды в строке:

printf -v DulicityCMD 'duplicity --log-file %s --progress --no-encryption --include="/etc" --include="/var/log" --include="/var/db" --include="/var/www" --include="/home" --exclude="**" / scp://duplicity@myhost.com:22//home/duplicity/backups/%s' $LOG_FILE $VPSNAME

echo $DulicityCMD

# THIS
$DulicityCMD

# OR THIS
sudo $DulicityCMD

Если вы отображаете содержание $DulicityCMD с echo это будет выглядеть примерно так:

duplicity --log-file /var/log/duplicity/backup.log --progress --no-encryption --include="/etc" --include="/var/log" --include="/var/db" --include="/var/www" --include="/home" --exclude="**" / scp://duplicity@myhost.com:22//home/duplicity/backups/myhost

При вводе этой команды, точно так же, как отображается с echoвсе работает отлично. Тем не менее, при запуске его из переменной, в пределах bash, это не работает.

Даже если я буду управлять этим с sudoили как пользователь root, он не работает. Вывод ошибки таков:

Traceback (most recent call last):
  File "/bin/duplicity", line 1546, in <module>
    with_tempdir(main)
  File "/bin/duplicity", line 1540, in with_tempdir
    fn()
  File "/bin/duplicity", line 1375, in main
    action = commandline.ProcessCommandLine(sys.argv[1:])
  File "/usr/lib64/python2.7/site-packages/duplicity/commandline.py", line 1131, in ProcessCommandLine
    set_selection()
  File "/usr/lib64/python2.7/site-packages/duplicity/commandline.py", line 969, in set_selection
    sel.ParseArgs(select_opts, select_files)
  File "/usr/lib64/python2.7/site-packages/duplicity/selection.py", line 268, in ParseArgs
    self.add_selection_func(self.glob_get_sf(arg, 1))
  File "/usr/lib64/python2.7/site-packages/duplicity/selection.py", line 434, in glob_get_sf
    sel_func = self.glob_get_filename_sf(glob_str, include)
  File "/usr/lib64/python2.7/site-packages/duplicity/selection.py", line 485, in glob_get_filename_sf
    raise FilePrefixError(filename)
FilePrefixError: "/etc"

Когда я использую evalЯ не получаю эту ошибку, но она теряет права root и не работает должным образом. Потому что мне нужно использовать вывод этого в сочетании с другой командой (sendmail), используя |,

В чем проблема и как я могу это исправить?

2 ответа

Ответ Стивена Рауха описывает проблему. У меня есть другое решение.

Но сначала: почему вы помещаете такую ​​длинную команду в переменную? Просто чтобы вы могли его распечатать? Почему бы просто не использовать опцию verbose (команды show) оболочки?

 установить -v
(your_command)
set + v 

x опция (трассировка выполнения) похожа; попробуйте выше с set -x а также set +x чтобы увидеть разницу. Если вы любите их обоих, вы можете объединить их с set -vx а также set +vx,


Довольно плохая идея построить строку из последовательности слов, разделенных пробелами, а затем попытаться разбить ее на части, чтобы восстановить исходные слова компонента. Вы столкнулись с одной из ловушек. Еще одна теоретическая возможность, что имя файла может содержать пробел (ы); было бы невозможно сказать из командной строки cat foo bar baz тот foo bar это одно имя файла и baz другой (и вы видели, что построение строки как cat "foo bar" baz тоже плохо работает). Эти и другие вопросы обсуждаются здесь:

Кроме того, вам не нужно printf просто для объединения строк.

Если вы действительно хотите собрать командную строку в хранилище и используете bash, вы можете использовать переменную массива, например:

DulicityCMD=(duplicity --log-file "$LOG_FILE" --progress --no-encryption --include="/etc" --include="/var/log" --include="/var/db" --include="/var/www" --include="/home" --exclude="**" / scp://duplicity@myhost.com:22//home/duplicity/backups/"$VPSNAME")

Обратите внимание, что я положил $LOG_FILE а также $VPSNAME в кавычки. Вы всегда должны цитировать все ссылки на переменные оболочки, если у вас нет веских причин не делать этого, и вы уверены, что знаете, что делаете. Но Стивен прав: вам не нужно заключать в кавычки постоянные строки "/etc", "/var/log", "/var/db", и так далее.  (Но, конечно, вам нужно процитировать "**", так как * это особый характер.)

Для удобства чтения вы можете разбить вышеперечисленное на несколько строк. Обратите внимание, что первая строка говорит = и последующие строки говорят += потому что они добавляют к тому, что было раньше:

DulicityCMD=(duplicity --log-file "$LOG_FILE" --progress --no-encryption)
DulicityCMD+=(--include="/etc" --include="/var/log" --include="/var/db")
DulicityCMD+=(--include="/var/www" --include="/home" --exclude="**")
DulicityCMD+=(/ scp://duplicity@myhost.com:22//home/duplicity/backups/"$VPSNAME")

Тогда вы можете сделать

 printf "% s \ n" "$ {DulicityCMD [*]}" (чтобы напечатать команду)
"$ {DulicityCMD [@]}" (для выполнения команды) 

или использовать sudo "${DulicityCMD[@]}", Если вам нужно. Смотрите bash(1) для получения дополнительной информации о синтаксисе bash, и мой ответ здесь, чтобы узнать больше об этой технике массива.

Проблема:

Это проблема правильного цитирования строки. Когда вы запускаете из командной строки что-то вроде:

duplicity --include="/etc"

"/etc" будет передан в основную программу как /etc поскольку "Это будет раздето снарядом. Но когда вы делаете что-то вроде:

DuplicityCMD='duplicity --include="/etc"'
$DuplicityCMD

"/etc" передается именно так. Это связано с тем, что, когда вы определили вашу переменную DuplicityCMDВы явно спросили, что " быть включенным, заключив его в ', Я бы предложил удалить " поскольку они выглядят в значительной степени ненужными.

( Подробнее: Как избежать кавычек в оболочке?)

Другие вопросы по тегам