Вопрос или проблема
В чем разница между этими тремя командами?
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
в вашем случае), это расширение дополнительно подлежит:
-
Разделению слов: то есть “вывод
date
с удаленными конечными символами новой строки” разделяется в соответствии с текущим значением переменной$IFS
(по умолчанию содержащей пробел, табуляцию и новую строку (и NUL сzsh
)) на несколько слов.Например, если
date
выводитFri 1 Nov 14:11:15 GMT 2013\n
(как это часто бывает в английской локали и в часовом поясе Великобритании), и$IFS
в настоящее время содержит:
, то это будет разделено на 3 слова:Fri 1 Nov 14
,11
и15 GMT 2013
. -
Генерации имен файлов (также известной как глоббинг) (кроме
zsh
): то есть, каждое слово, образованное в результате предыдущего разделения, проверяется на наличие подстановочных символов (*
,?
,[...]
, хотя некоторые оболочки содержат больше), и расширяется до списка имен файлов, которые соответствуют этим шаблонам. Например, если выводdate
—?%? 33 */*/* UVC 3432
(как это часто бывает в венерианских локалях и в часовом поясе UVC), и$IFS
— значение по умолчанию), то это расширяется до всех не скрытых имен файлов из 3 символов в текущем каталоге, у которых средний символ%
,33
, все не скрытые файлы во всех не скрытых подкаталогах всех не скрытых подкаталогов текущего каталога,UVC
и3432
.
Поэтому:
- Всегда цитируйте (двойными кавычками) подстановки команд, если вам не нужно либо разделение слов, либо генерация имен файлов, выполняемая при его расширении
- Если вам нужно разделение слов, то вам следует установить
$IFS
на символы, по которым вы хотите выполнить разделение. - Кроме
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. Эти различия по сути заключаются в трактовке кавычек и управляющих символов.
Теория
-
echo \
date“: Командаdate
будет выполнена, и ее вывод будет подставлен на место указанного выражения. Результат выполнения подстановки команды будет подвергаться разбиению на слова, то есть пробелы и специальные символы могут привести к разбиению на несколько аргументов дляecho
. Это происходит из-за двойной обработки — подстановки команды и разбиения на слова. -
echo "\
date`"`: Здесьdate
также будет выполнена, и вывод этой команды будет подставлен, однако весь результат будет считаться как единый аргумент дляecho
благодаря использованию двойных кавычек. Двойные кавычки в Unix и Linux автоматах рассматриваются как "слабые" кавычки, они позволяют выполнять подстановку переменных и команд, но защищают пробелы и другие символы от разбиения на отдельные слова. -
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.