В чем разница между echo date, echo "date" и echo ‘date‘?

Вопрос или проблема

В чем разница между этими тремя командами?

echo `date`
echo "`date`"
echo '`date`'

Я запутался, в чем именно разница. Я думаю, что когда вокруг стоят ‘ это значит, что это строка, следовательно echo буквально выведет строку date вместо отображения даты?

`date` просто расширится до вывода команды date. Однако, он удаляет лишние пробелы в местах, где в выводе есть более одного подряд идущего пробела. (Это происходит потому, что подстановка команд подлежит разделению слов и потому, как команда echo обрабатывает несколько аргументов.)

В “`date`” двойные кавычки являются слабыми кавычками, поэтому они будут расширять переменные (попробуйте “$PWD”) и выполнять подстановку команд. Результат расширения передается команде echo как один аргумент со всеми последовательными пробелами: словоразделение не выполняется.

В ‘`date`’ одинарные кавычки являются более сильными кавычками, поэтому они не позволяют выполнять расширение переменных или подстановку команд внутри них.

Обратитесь к этой ссылке для более подробного объяснения.

Отредактировано первый пункт как указал Michael Suelmann в комментарии ниже.

Обе

echo `date`

и

echo "`date`"

выведут дату. Вывод последней выглядит как вывод от выполнения date самостоятельно.

Однако есть разница: та, которая окружена в кавычки " ", будет отправлена в echo как один аргумент. Кавычки обрамляют вывод всей команды как один аргумент. Поскольку echo просто печатает свои аргументы в порядке с пробелами между ними, он будет выглядеть, в основном, одинаково.

Вот пример тонкой разницы:

echo `date`

производит:

Fri Nov 1 01:48:45 EST 2013

но:

echo "`date`"

производит:

Fri Nov  1 01:48:49 EST 2013

Обратите внимание, что два пробела после Nov были сокращены до одного без кавычек. Это потому, что оболочка анализирует каждый элемент, разделенный пробелом, и отправляет результат в echo как 6 аргументов. Когда вы цитируете, echo получает один единый аргумент, и кавычки сохраняют пробел.

Это становится гораздо более важным в других командах, кроме echo. Например, представьте себе команду foo, которая хочет два аргумента: дату и адрес электронной почты.

Это сработает в этом сценарии:

foo "`date`" [email protected]

Но это запутает сценарий, отправив ему 7 аргументов:

foo `date` [email protected]

В оболочках POSIX, `date` — это древняя форма подстановки команд. Современный синтаксис — $(date).

В обоих случаях они разворачиваются в вывод date с удаленными конечными символами новой строки (при условии, что вывод не содержит символов NUL).

Однако, если не в двойных кавычках и в контексте списка (например, в аргументах к простым командам, таким как echo в вашем случае), это расширение дополнительно подлежит:

  1. Разделению слов: то есть “вывод date с удаленными конечными символами новой строки” разделяется в соответствии с текущим значением переменной $IFS (по умолчанию содержащей пробел, табуляцию и новую строку (и NUL с zsh)) на несколько слов.

    Например, если date выводит Fri 1 Nov 14:11:15 GMT 2013\n (как это часто бывает в английской локали и в часовом поясе Великобритании), и $IFS в настоящее время содержит :, то это будет разделено на 3 слова: Fri 1 Nov 14, 11 и 15 GMT 2013.

  2. Генерации имен файлов (также известной как глоббинг) (кроме zsh): то есть, каждое слово, образованное в результате предыдущего разделения, проверяется на наличие подстановочных символов (*, ?, [...], хотя некоторые оболочки содержат больше), и расширяется до списка имен файлов, которые соответствуют этим шаблонам. Например, если вывод date?%? 33 */*/* UVC 3432 (как это часто бывает в венерианских локалях и в часовом поясе UVC), и $IFS — значение по умолчанию), то это расширяется до всех не скрытых имен файлов из 3 символов в текущем каталоге, у которых средний символ %, 33, все не скрытые файлы во всех не скрытых подкаталогах всех не скрытых подкаталогов текущего каталога, UVC и 3432.

Поэтому:

  1. Всегда цитируйте (двойными кавычками) подстановки команд, если вам не нужно либо разделение слов, либо генерация имен файлов, выполняемая при его расширении
  2. Если вам нужно разделение слов, то вам следует установить $IFS на символы, по которым вы хотите выполнить разделение.
  3. Кроме zsh, если вам нужно разделение слов, но не генерация имен файлов, вам необходимо выполнить команду set -o noglob (также известную как set -f), чтобы отключить ее.

Одинарные кавычки цитируют все, поэтому символы обратные апострофы воспринимаются буквально.

Пример (использование -o xtrace облегчает понимание происходящего):

$ bash --norc -o xtrace
bash-4.2$ IFS=:
+ IFS=:
bash-4.2$ echo `date`
++ date
+ echo 'Fri  1 Nov 14' 42 '33 GMT 2013'
Fri  1 Nov 14 42 33 GMT 2013
bash-4.2$ echo "`date`"
++ date
+ echo 'Fri  1 Nov 14:42:41 GMT 2013'
Fri  1 Nov 14:42:41 GMT 2013

bash-4.2$ cd /lib/modules
+ cd /lib/modules
bash-4.2$ export TZ=UVC LC_ALL=vs_VS
+ export TZ=UVC LC_ALL=vs_VS
+ TZ=UVC
+ LC_ALL=vs_VS
bash-4.2$ unset -v IFS     # верните поведение по умолчанию
+ unset -v IFS
bash-4.2$ echo `date`
++ date
+ echo '?%?' 33 3.10-2-amd64/build/arch 3.10-2-amd64/build/include 3.10-2-amd64/build/Makefile 3.10-2-amd64/build/Module.symvers 3.10-2-amd64/build/scripts 3.10-2-amd64/kernel/arch 3.10-2-amd64/kernel/crypto 3.10-2-amd64/kernel/drivers 3.10-2-amd64/kernel/fs 3.10-2-amd64/kernel/lib 3.10-2-amd64/kernel/mm 3.10-2-amd64/kernel/net 3.10-2-amd64/kernel/sound 3.10-2-amd64/source/arch 3.10-2-amd64/source/include 3.10-2-amd64/source/Makefile 3.10-2-amd64/source/scripts 3.10-2-amd64/updates/dkms 3.10-3-amd64/build/arch 3.10-3-amd64/build/include 3.10-3/amkdfdf/block 3.10-3-amd64/build/Makefile 3.10-3-amd64/build/Module.symvers 3.10-3-amd64/build/scripts 3.10-3-amd64/kernel/arch 3.10-3-amd64/kernel/crypto 3.10-3-amd64/kernel/drivers 3.10-3-amd64/kernel/fs 3.10-3-amd64/kernel/lib 3.10-3-amd64/kernel/mm 3.10-3-amd64/kernel/net 3.10-3-amd64/kernel/sound 3.10-3-amd64/source/arch 3.10-3-amd64/source/include 3.10-3-amd64/source/Makefile 3.10-3-amd64/source/scripts 3.10-3-amd64/updates/dkms UVC 3432
?%? 33 3.10-2-amd64/build/arch 3.10-2-amd64/build/include 3.10-2-amd64/build/Makefile 3.10-2-amd64/build/Module.symvers 3.10-2-amd64/build/scripts 3.10-2-amd64/kernel/arch 3.10-2-amd64/kernel/crypto 3.10-2-amd64/kernel/drivers 3.10-2-amd64/kernel/fs 3.10-2-amd64/kernel/lib 3.10-2-amd64/kernel/mm 3.10-2-amd64/kernel/net 3.10-2-amd64/kernel/sound 3.10-2-amd64/source/arch 3.10-2-amd64/source/include 3.10-2-amd64/source/Makefile 3.10-2-amd64/source/scripts 3.10-2-amd64/updates/dkms 3.10-3-amd64/build/arch 3.10-3-amd64/build/include 3.10-3-amd64/build/Makefile 3.10-3-amd64/build/Module.symvers 3.10-3-amd64/build/scripts 3.10-3-amd64/kernel/arch 3.10-3-amd64/kernel/crypto 3.10-3-amd64/kernel/drivers 3.10-3-amd64/kernel/fs 3.10-3-amd64/kernel/lib 3.10-3-amd64/kernel/mm 3.10-3-amd64/kernel/net 3.10-3-amd64/kernel/sound 3.10-3-amd64/source/arch 3.10-3-amd64/source/include 3.10-3-amd64/source/Makefile 3.10-3-amd64/source/scripts 3.10-3-amd64/updates/dkms UVC 3432
bash-4.2$ echo "`date`"
++ date
+ echo '?%? 33 */*/* UVC 3432'
?%? 33 */*/* UVC 3432

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

С помощью `date` вы получаете вывод date, разбитый на несколько слов, потому что разделение слов выполняется после подстановки команд.

С “`date`” вы получаете вывод date как одно слово/параметр, поскольку существует командная подстановка внутри двойных кавычек, но вывод дальше не анализируется. То же самое верно для расширения переменных, таких как “$i”, в моем примере ниже.

С ‘`date`’ вы получаете буквальный `date`, так как нет подстановки команд в одинарных кавычках.

Возможно, различия в трех формах станут более заметными таким образом:

> for i in `date`; do echo "$i"; done
Fr
1.
Nov
12:25:30
CET
2013

> for i in "`date`"; do echo "$i"; done
Fr 1. Nov 12:25:38 CET 2013

> for i in '`date`'; do echo "$i"; done
`date`

Ответ или решение

Вопрос о том, какая разница между командами echo \date`,echo "`date`", иecho ‘`date`’`, связан с особенностями обработки строк и подстановки команд в командных оболочках Unix и Linux. Эти различия по сути заключаются в трактовке кавычек и управляющих символов.

Теория

  1. echo \date“: Команда date будет выполнена, и ее вывод будет подставлен на место указанного выражения. Результат выполнения подстановки команды будет подвергаться разбиению на слова, то есть пробелы и специальные символы могут привести к разбиению на несколько аргументов для echo. Это происходит из-за двойной обработки — подстановки команды и разбиения на слова.

  2. echo "\date`"`: Здесь date также будет выполнена, и вывод этой команды будет подставлен, однако весь результат будет считаться как единый аргумент для echo благодаря использованию двойных кавычек. Двойные кавычки в Unix и Linux автоматах рассматриваются как "слабые" кавычки, они позволяют выполнять подстановку переменных и команд, но защищают пробелы и другие символы от разбиения на отдельные слова.

  3. echo '\date`’`: В этом случае одинарные кавычки полностью защищают содержимое от какой-либо обработки. Команда date не будет выполняться, и echo просто выведет строку \date“ как есть. Одинарные кавычки в Unix и Linux системах — это "сильные" кавычки, которые предотвращают подстановку команд и переменных.

Пример

Для иллюстрации основных отличий рассмотрим несколько примеров:

  • Пример 1

    $ echo `date`

    Этот вызов команды может привести, например, к следующему выводу: Fri Nov 1 12:30:00 GMT 2023. Каждый элемент строки (день недели, месяц, число и т. д.) будет рассматриваться как отдельный аргумент, если переменная окружения IFS (Internal Field Separator) не модифицирована.

  • Пример 2

    $ echo "`date`"

    Этот вызов команды даст результат похожий на date без каких-либо изменений: Fri Nov 1 12:30:00 GMT 2023. Весь вывод будет передан как один аргумент, благодаря двойным кавычкам.

  • Пример 3

    $ echo '`date`'

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

Применение

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

  • Когда требуется управлять выводом команд и сохранять пробелы, как, например, при генерации сложных строковых значений для файлов конфигурации, рекомендуется использовать двойные кавычки.

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

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

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

Таким образом, грамотная работа с кавычками в shell environment не только повышает надежность скриптов, но и делает код более понятным и легким в сопровождении. Это знание является неотъемлемой частью профессиональной практики администратора систем или разработчика, работающего в пользовательских средах Unix.

Оцените материал
Добавить комментарий

Капча загружается...