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

801
guinalz

Я использую 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) с помощью |.

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

0
См. [BashFAQ # 50: я пытаюсь поместить команду в переменную, но сложные случаи всегда терпят неудачу!] (Http://mywiki.wooledge.org/BashFAQ/050). Gordon Davisson 7 лет назад 0

2 ответа на вопрос

0
Stephen Rauch

Проблема:

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

duplicity --include="/etc" 

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

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

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

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

(1) Попробуйте еще раз. Команда, которую вы показываете, будет (пытаться) выполнить команду с именем `--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 / $ VPSNAM` и переменная окружения `DuplicityCMD` установлена ​​в` duplicity`. (2) Если у человека возникли проблемы с цитатами, не предлагайте удалять цитаты, кроме как в крайнем случае. G-Man 7 лет назад 0
@ G-Man 1) Спасибо, 2) Что? Stephen Rauch 7 лет назад 0
Какие? Как обсуждалось в [Последствия для безопасности при забывании в кавычках переменной в оболочках bash / POSIX] (// unix.stackexchange.com/q/171346/80216), у нас есть текущие проблемы здесь [SU] и [Unix & Linux Stack Exchange] (// unix.stackexchange.com) людей, не цитирующих вещи, которые они должны цитировать - например, guinalz не цитировал `$ LOG_FILE` и` $ VPSNAME`. Но ваш ответ выглядит так, как будто вы говорите: «Это проблема правильного цитирования строки. ... Я бы предложил удалить "" `...". Мы не хотим учить людей * удалять * цитаты, когда у них есть проблемы. G-Man 7 лет назад 0
PS Ваша команда сейчас не выполняется из-за `--exclude = '**'`. G-Man 7 лет назад 0
@ G-Man. Спасибо за информацию, полезно. Еще больше знаний об этом месте. Только был здесь месяц, было интересно. Stephen Rauch 7 лет назад 0
0
G-Man

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

Но сначала: почему вы помещаете такую ​​длинную команду в переменную? Просто чтобы вы могли его распечатать? Почему бы просто не использовать параметр оболочки 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" "$ " (для печати команды) "$ " (для выполнения команды)

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