Проблема Bash с eval, переменными и кавычками - PullRequest
12 голосов
/ 10 марта 2011

Я читал о кавычках в bash здесь и везде, но мне не помогло решить эту проблему.

Дело в том, что у меня есть небольшой скрипт для создания резервных копий в цикле.

Если я не использую eval, тогда у меня проблемы с переменной $OPTIONS в rsync.

Но если я использую eval, тогда проблема переходит к переменной $CURRENT_DIR ...

rsync возвращает следующее сообщение: «Неожиданный локальный аргумент: / путь / с»

Я пробовал каждый способ цитирования переменной $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 ]

16 голосов
/ 10 марта 2011
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 действует так, как если бы вы набрали command "some thing".

Внешние кавычки в моей команде не обязательны, они просто привычка. Вы также можете использовать:

eval rsync --delete-excluded -i $OPTIONS  root@example.com$f \"$CURRENT_DIR/xxx/$DIR/files\"
7 голосов
/ 10 марта 2011

Использование eval опасно, и его следует по возможности избегать.В этом случае основная проблема заключается в том, что вы пытаетесь определить OPTIONS как содержащие несколько слов, а переменные bash справляются с этим не очень хорошо.Существует решение: поместите OPTIONS в массив вместо простой переменной (и используйте двойные кавычки вокруг всех ссылок на переменные, чтобы пробелы не обрабатывались как разделители слов).

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"
3 голосов
/ 10 марта 2011

Я согласен с Гордоном

В этом случае вам не нужно eval (вы не формируете имя переменной из переменных или не делаете выражения на лету)

И вы хотите заключить в кавычки все ссылки на переменные, которые МОГУТ иметь пробелы, которые вы хотели бы сохранить

Но другая хорошая привычка - всегда ссылаться на переменные с помощью {} ...

  "${CURRENT_DIR}" 

вместо

  $CURRENT_DIR

Это устраняет любую двусмысленность имени

1 голос
/ 29 сентября 2018

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

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

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

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 "$(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 не зло - это просто неправильно понято:)

0 голосов
/ 10 марта 2011

Вам нужно выйти из пробела в CURRENT_DIR="/path/with\ spaces/backup", если это не сработает, тогда поставьте двойную обратную косую черту CURRENT_DIR="/path/with\\ spaces/backup"

0 голосов
/ 10 марта 2011

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

...