Запустите в 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
тоже плохо работает). Эти и другие вопросы обсуждаются здесь:
- Почему мой сценарий оболочки заглушает пробелы или другие специальные символы?
- Последствия для безопасности: забыть заключить переменную в оболочку bash/POSIX
Кроме того, вам не нужно 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
Вы явно спросили, что "
быть включенным, заключив его в '
, Я бы предложил удалить "
поскольку они выглядят в значительной степени ненужными.