Я использую 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 ответа2

0

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

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

set -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[*]}"           (to print the command)
"${DulicityCMD[@]}"                         (to execute the command)

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

0

Проблема:

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

duplicity --include="/etc"

"/etc" и т.д.» будут переданы к основной программе в /etc и т.д. , потому что " будут были лишены оболочкой. Но когда вы делаете что-то вроде:

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

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

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .