Проблема 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-->


Я знаю, вероятно, ypu уже использовал его, но, как насчет одинарных кавычек? (этот "тип") ?


универсальное многоразовое решение

хотя понимание того, как правильно цитировать вещи важно, для простоты использования и предотвращения ошибок я предпочитаю использовать функцию:

следующее сохраняет пробелы в аргументах, цитируя каждый элемент массива:

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"