Почему параметры в кавычковой переменной не работают, но работают при некорректном?

Я читал о том, что я должен приводить переменные в bash, например «$ foo» вместо $ foo. Однако при написании сценария я нашел случай, когда он работает без кавычек, но не с ними:

wget_options='--mirror --no-host-directories' local_root="$1" # ./testdir recieved from command line remote_root="$2" # ftp://XXX recieved from command line relative_path="$3" # /XXX received from command line 

Это работает:

 wget $wget_options --directory_prefix="$local_root" "$remote_root$relative_path" 

Этого нет (обратите внимание на двойные кавычки aroung $ wget_options):

 wget "$wget_options" --directory_prefix="$local_root" "$remote_root$relative_path" 
  • Что является причиной этого?

  • Является ли первая строка хорошей версией; или я должен подозревать, что где-то скрытая ошибка вызывает такое поведение?

  • В общем, где я могу найти хорошую документацию, чтобы понять, как работает bash и его цитирование? Во время написания этого сценария мне кажется, что я начал работать над базой проб и ошибок вместо понимания правил.

3 Solutions collect form web for “Почему параметры в кавычковой переменной не работают, но работают при некорректном?”

В принципе, вы должны удвоить разметку переменных переменных, чтобы защитить их от разделения слов (и генерации имени файла). Однако, в вашем примере,

 wget_options='--mirror --no-host-directories' wget $wget_options --directory_prefix="$local_root" "$remote_root$relative_path" 

расщепление слов – именно то, что вы хотите .

С помощью "$wget_options" (цитируется) wget не знает, что делать с единственным аргументом --mirror --no-host-directories и жалуется

 wget: unknown option -- mirror --no-host-directories 

Для wget чтобы увидеть два параметра --mirror и --mirror --no-host-directories как отдельные, должно произойти разбиение слов.

Есть более надежные способы сделать это. Если вы используете bash или любую другую оболочку, которая использует массивы типа bash do, см . Ответ glenn jackman . Ответ Гилла дополнительно описывает альтернативное решение для более простых оболочек, таких как стандарт /bin/sh .

Связанный вопрос с хорошими ответами: Почему мой сценарий оболочки задыхается от пробелов или других специальных символов?


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

Также есть несколько случаев, когда вам не нужно указывать разложения переменных. Но в любом случае проще использовать двойные кавычки, так как это не имеет большого значения. Одним из таких случаев является

 variable=$other_variable 

Другой

 case $variable in ...) ... ;; esac 

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

 wget_options=( --mirror --no-host-directories --directory_prefix="$1" ) wget "${wget_options[@]}" "$2/$3" 

Вы пытаетесь сохранить список строк в строковой переменной. Это не подходит. Независимо от того, как вы обращаетесь к переменной, что-то нарушается.

wget_options='--mirror --no-host-directories' устанавливает переменную wget_options в строку, содержащую пробел. На этом этапе невозможно определить, должно ли пространство быть частью опции или разделителем между опциями.

Когда вы wget "$wget_options" к переменной с цитируемой заменой wget "$wget_options" , значение переменной используется как строка. Это означает, что он передается как единственный параметр для wget , поэтому это единственный параметр. Это ломается в вашем случае, потому что вы предполагали, что это означает несколько вариантов.

Когда вы используете некотируемую замену wget $wget_options , значение переменной строки претерпевает процесс расширения с wget $wget_options «split + glob»:

  1. Возьмите значение переменной и разделите ее на части, разделенные пробелами (если вы не изменили переменную $IFS ). Это приводит к промежуточному списку строк.
  2. Для каждого элемента промежуточного списка, если он является шаблоном подстановки, который соответствует одному или нескольким файлам, замените этот элемент на список соответствующих файлов.

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

В ksh, bash, yash и zsh вы можете использовать переменную массива. Массив в терминологии оболочки представляет собой список строк, поэтому нет потери информации. Чтобы создать переменную массива, поместите скобки вокруг элементов массива при назначении значения переменной. Чтобы получить доступ ко всем элементам массива, используйте "${ VARIABLE [@]}" – это обобщение "$@" , которое формирует список из элементов массива. Обратите внимание, что здесь нужны двойные кавычки, иначе каждый элемент подвергается split + glob.

 wget_options=(--mirror --no-host-directories --user-agent="I can haz spaces") wget "${wget_options[@]}" … 

В plain sh нет переменных массива. Если вы не против потери позиционных аргументов, вы можете использовать их для хранения одного списка строк.

 set -- --mirror --no-host-directories --user-agent="I can haz spaces" wget "$@" … 

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

  • Как включить $ sign как строку?
  • Как форматировать десятичное число, чтобы отображать прецизионные ноль, но не конечные?
  • Как узнать, где установлены переменные и функции оболочки?
  • Пространства в назначениях переменных в сценариях оболочки
  • Синтаксис синтаксиса подстановки команд в bash - какой из этих двух методов лучше?
  • Использование вывода команды `grep` как переменной во второй команде
  • Какие ситуации существуют, когда переменные Bash не должны быть двойными кавычками?
  • Почему у ] и ] есть другой синтаксис?
  • Bash - ввод данных для замещенной команды, хранящейся в переменной
  • Инициализация локальной переменной Bash
  • Область локальных переменных в функциях оболочки
  • Linux и Unix - лучшая ОС в мире.