Проблема Bash с eval, переменными и кавычками
Я читал о цитатах в bash здесь и везде, но у меня нет помощи в решении этой проблемы.
дело в том, что у меня есть небольшой скрипт для выполнения резервного копирования в цикле.
если я не использую eval
тогда у меня проблемы с $OPTIONS
переменная rsync
.
но если я использую eval
затем проблема переходит в переменную $CURRENT_DIR
...
rsync возвращает следующее сообщение: 'неожиданный локальный arg: / путь / с'
Я пробовал каждый способ цитирования переменной $CURRENT_DIR
CURRENT_DIR="/path/with spaces/backup"
DIR="dir_by_project"
f=":/home/project_in_server"
OPTIONS="-avr --exclude 'public_html/cms/cache/**' --exclude 'public_html/cms/components/libraries/cmslib/cache/**' --delete"
eval rsync --delete-excluded -i $OPTIONS root@example.com$f $CURRENT_DIR/xxx/$DIR/files
есть ли способ, что я могу использовать переменную $CURRENT_DIR
без проблем, порождаемых пробелами?
7 ответов
eval rsync --delete-excluded -i $OPTIONS root@example.com$f "\"$CURRENT_DIR/xxx/$DIR/files\""
command "some thing"
выполняет команду с одним аргументом some thing
. Кавычки анализируются оболочкой, а аргументы настраиваются как массив при выполнении команды. Команда увидит аргумент как нечто без кавычек.
команда eval обрабатывает свои аргументы более или менее так, как если бы они были введены в оболочку. Итак, если вы eval command "some thing"
, bash выполняет eval
С двумя аргументами: command
и some thing
(снова цитаты съедены, а bash устанавливает массив аргументов). Таким образом, eval действует так, как будто вы набрали command some thing
в оболочке, что не то, что вы хотите.
то, что я сделал, было просто избежать кавычек, так что bash проходит буквально "некоторые вещи" включая цитаты для eval. затем eval действует так, как будто вы набрали command "some thing"
.
внешние кавычки в моей команде строго не требуются, они просто привычка. Вы могли бы также использовать:
eval rsync --delete-excluded -i $OPTIONS root@example.com$f \"$CURRENT_DIR/xxx/$DIR/files\"
использование eval опасен, и его следует избегать, когда это возможно. В этом случае основная проблема заключается в том, что вы пытаетесь определить параметры как содержащие несколько слов, а переменные bash не очень хорошо справляются с этим. Существует решение: поместите параметры в массив вместо простой переменной (и используйте двойные кавычки вокруг всех ссылок на переменные, чтобы пробелы не рассматривались как разделители слов).
CURRENT_DIR="/path/with spaces/backup"
DIR="dir_by_project"
f=":/home/project_in_server"
OPTIONS=(-avr --exclude 'public_html/cms/cache/**' --exclude 'public_html/cms/components/libraries/cmslib/cache/**' --delete)
rsync --delete-excluded -i "${OPTIONS[@]}" "root@example.com$f" "$CURRENT_DIR/xxx/$DIR/files"
Я согласен с Гордоном
в этом случае у вас нет необходимости в eval (вы не формируете имя переменной из переменных или иным образом делаете выражение на лету)
и вы хотите дважды процитировать все ссылки на переменные, которые могут иметь пробелы, которые вы хотите сохранить
но еще одна хорошая привычка-всегда эталонная переменные в {} ...
"${CURRENT_DIR}"
вместо
$CURRENT_DIR
Это удаляет любое имя двусмысленность!--3-->
универсальное многоразовое решение
хотя понимание того, как правильно цитировать вещи важно, для простоты использования и предотвращения ошибок я предпочитаю использовать функцию:
следующее сохраняет пробелы в аргументах, цитируя каждый элемент массива:
function token_quote {
local quoted=()
for token; do
quoted+=( "$(printf '%q' "$token")" )
done
printf '%s\n' "${quoted[*]}"
}
пример использования:
$ token_quote token 'single token' token
token single\ token token
выше, обратите внимание на single token
пространство цитируется как \
.
$ set $(token_quote token 'single token' token)
$ eval printf '%s\n' "$@"
token
single token
token
$
это показывает, что лексемы действительно отмежеваться.
учитывая некоторые ненадежные пользовательские данные:
% input="Trying to hack you; date"
создайте команду для eval:
% cmd=(echo "User gave:" "$input")
Eval его, с казалось бы правильное цитирование:
% eval "$(echo "${cmd[@]}")"
User gave: Trying to hack you
Thu Sep 27 20:41:31 +07 2018
обратите внимание, что вы были взломаны. date
был выполнен, а не напечатан буквально.
вместо с token_quote()
:
% eval "$(token_quote "${cmd[@]}")"
User gave: Trying to hack you; date
%
eval
это не зло - это просто неправильно понято:)
вам нужно избежать пространства в CURRENT_DIR="/path/with\ spaces/backup"
Если это не работает, то поместите двойную обратную косую черту CURRENT_DIR="/path/with\ spaces/backup"