Я пытаюсь понять, как именно Bash обрабатывает следующую строку:
$(< "$FILE")
Согласно странице руководства Bash, это эквивалентно:
$(cat "$FILE")
и я могу следовать линии рассуждений для этой второй строки. Bash выполняет раскрытие переменной $FILE
, вводит подстановку команды, передает значение $FILE
to cat
, cat выводит содержимое $FILE
на стандартный вывод, подстановка команды завершается заменой всей строки стандартным выводом, полученным из команды внутри, и Bash пытается выполнить ее следующим образом: простая команда.
Однако для первой строки, о которой я упоминал выше, я понимаю это так: Bash выполняет подстановку переменных $FILE
, Bash открывается $FILE
для чтения на стандартном вводе, каким-то образом стандартный ввод копируется в стандартный вывод , подстановка команд завершается, и Bash пытается выполнить результирующий стандарт. вывод.
Может кто-нибудь объяснить мне, как содержимое $FILE
от стандартного ввода к стандартному выводу?
источник
bash
будет интерпретировать это какcat filename
», вы имеете в виду, что это поведение относится к подстановке команд? Потому что, если я запускаю< filename
сам, bash не догонит его. Он ничего не выдаст и вернет меня обратно к приглашению.cat < filename
противcat filename
которого я выступаю, и могу вернуться.|
создает канал между двумя подпроцессами (или, с некоторыми оболочками, из подпроцесса в стандартный ввод оболочки). Оператор оболочки$(…)
создает канал от подпроцесса к самой оболочке (не к его стандартному вводу). Оператор оболочки<
не использует конвейер, он только открывает файл и перемещает дескриптор файла в стандартный ввод.< file
это не то же самое, что иcat < file
(кроме случаев,zsh
когда это похоже$READNULLCMD < file
).< file
идеально подходит для POSIX и просто открываетfile
для чтения, а затем ничего не делает (такfile
что близко сразу). Это$(< file)
или`< file`
это специальный операторksh
,zsh
иbash
(и поведение не определено в POSIX). Смотрите мой ответ для деталей.$(cmd1) $(cmd2)
как правило, будет то же самое, что и$(cmd1; cmd2)
. Но посмотрите на тот случай, когдаcmd2
есть< file
. Если мы говорим$(cmd1; < file)
, файл не читается, но, с$(cmd1) $(< file)
, это так. Поэтому неправильно говорить,$(< file)
что это обычный случай$(command)
с командой< file
.$(< …)
это особый случай подстановки команд, а не обычное использование перенаправления.$(<file)
(также работает с`<file`
) - специальный оператор оболочки Korn, скопированный с помощьюzsh
иbash
. Это действительно похоже на подстановку команд, но это не совсем так.В оболочках POSIX простая команда:
Все части являются необязательными, вы можете иметь только перенаправления, только команды, только назначения или комбинации.
Если есть перенаправления, но нет команды, перенаправления выполняются (то есть,
> file
они открываются и усекаютсяfile
), но тогда ничего не происходит. ТакОткрывается
file
для чтения, но тогда ничего не происходит, так как нет команды. Так чтоfile
тогда закрыто и все. Если бы$(< file)
была простая подстановка команд , то она бы расширилась до нуля.В спецификации POSIX , в
$(script)
, еслиscript
состоит только из перенаправлений, что дает неопределенный результат . Это позволяет этому особому поведению оболочки Korn.В ksh (здесь протестировано с
ksh93u+
), если скрипт состоит из одной и только одной простой команды (хотя комментарии разрешены до и после), которая состоит только из перенаправлений (без команды, без назначения) и если первое перенаправление является stdin (fd 0) только перенаправление ввода<
,<<
или<<<
:$(< file)
$(0< file)
$(<&3)
(также$(0>&3)
фактически, поскольку это - тот же самый оператор)$(< file > foo 2> $(whatever))
но нет:
$(> foo < file)
$(0<> file)
$(< file; sleep 1)
$(< file; < file2)
затем
<&3
), минуя завершающие символы новой строки.как будто используя,
$(cat < file)
кроме этогоcat
$(<${file=foo.txt})
или$(<file$((++n)))
)В
zsh
это же самое , за исключением того , что особое поведение срабатывает только , когда есть только один вход Перенаправление файл (<file
или0< file
, нет<&3
,<<<here
,< a < b
...)Тем не менее, за исключением случаев эмуляции других оболочек, в:
то есть, когда есть только входные перенаправления без команд, вне подстановки команд
zsh
запускается$READNULLCMD
(пейджер по умолчанию), а при наличии как входных, так и выходных перенаправлений$NULLCMD
(cat
по умолчанию), так что даже если$(<&3)
он не распознается как специальный оператор, он все равно будет работать как вksh
, вызывая для этого пейджер (этот пейджер действует так,cat
как его stdout будет конвейером).Однако в то время как
ksh
«s$(< a < b)
будет расширяться на содержаниеa
, вzsh
, она расширяется к содержаниюa
иb
(или толькоb
еслиmultios
опция выключена),$(< a > b)
будет копироватьa
вb
и расширять ничего, и т.д.bash
имеет похожий оператор, но с некоторыми отличиями:комментарии разрешены до, но не после:
работает но:
расширяется в ничто.
как в
zsh
только один файл STDIN Перенаправление, хотя нет никакого падения назад к$READNULLCMD
, так$(<&3)
,$(< a < b)
действительно выполнять переадресацию , но расширить ничего.bash
и не вызываетcat
, он все еще разветвляется процесс, который передает содержимое файла через канал, что делает его гораздо меньше оптимизации, чем в других оболочках. По сути, это как$(cat < file)
гдеcat
бы был встроенcat
.$(<${file=foo.txt})
упомянутом выше случае это$file
назначение теряется впоследствии).In
bash
,IFS= read -rd '' var < file
(также работает вzsh
) - более эффективный способ чтения содержимого текстового файла в переменную. Это также имеет преимущество сохранения конечных символов новой строки. Смотрите также$mapfile[file]
вzsh
(вzsh/mapfile
модуле и только для обычных файлов), который также работает с двоичными файлами.Обратите внимание, что варианты на основе pdksh
ksh
имеют несколько вариаций по сравнению с ksh93. Интересно, чтоmksh
(одна из этих оболочек, полученных из pdksh), воптимизирован тем, что содержимое документа здесь (без завершающих символов) расширяется без использования временного файла или канала, как в случае с документами здесь, что делает его эффективным синтаксисом многострочного цитирования.
Быть переносимым на все версии
ksh
,zsh
иbash
, лучше всего, ограничиваться только$(<file)
избеганием комментариев и помнить, что изменения переменных, сделанные внутри, могут сохраняться или не сохраняться.источник
$(<)
это оператор на имена файлов? Является ли<
в качестве$(<)
оператора перенаправления, или не является оператором самостоятельно, и должен быть частью всего оператора$(<)
?$(<file)
предназначен для расширения до содержанияfile
таким же образом, как$(cat < file)
и. Как это делается, зависит от оболочки к оболочке, что подробно описано в ответе. Если хотите, вы можете сказать, что это специальный оператор, который запускается, когда то, что выглядит как подстановка команды (синтаксически), содержит то, что выглядит как одно перенаправление стандартного ввода (синтаксически), но опять же с предостережениями и вариациями в зависимости от оболочки, как указано здесь ,n<&m
иn>&m
делать то же самое? Я этого не знал, но, думаю, это не слишком удивительно.dup(m, n)
. Я вижу некоторые доказательства того, что ksh86 использует stdio, а некоторыеfdopen(fd, "r" or "w")
, возможно, имели значение тогда. Но использование stdio в оболочке не имеет большого смысла, поэтому я не ожидаю, что вы найдете какую-либо современную оболочку, в которой это будет иметь значение. Отличие заключается в том, что>&n
этоdup(n, 1)
(сокращение1>&n
), в то время как<&n
этоdup(n, 0)
(сокращенно0<&n
).dup2()
;dup()
принимает только один аргумент и, какopen()
, использует самый низкий доступный дескриптор файла. (Сегодня я узнал, что естьdup3()
функция .)Потому что
bash
делает это для вас внутренне, расширяет имя файла и выводит файл на стандартный вывод, как если бы вы это делали$(cat < filename)
. Это особенность bash, может быть, вам нужно изучитьbash
исходный код, чтобы точно знать, как он работает.Вот функция для обработки этой функции (из
bash
исходного кода, файлаbuiltins/evalstring.c
):Примечание,
$(<filename)
которое не совсем эквивалентно$(cat filename)
; последний потерпит неудачу, если имя файла начинается с тире-
.$(<filename)
был изначально изksh
, и был добавленbash
изBash-2.02
.источник
cat filename
потерпит неудачу, если имя файла начинается с тире, потому что cat принимает параметры. Вы можете обойти это на большинстве современных системcat -- filename
.Думайте о подстановке команд как о выполнении команды как обычно и выводе вывода в тот момент, когда вы запускаете команду.
foo=$(echo "bar")
установит значение переменной$foo
вbar
; вывод командыecho bar
.Подстановка команд
источник
$(< file)
, и он не нуждается в учебнике по общему случаю. Если вы говорите,$(< file)
что это обычный случай$(command)
с командой< file
, то вы говорите то же самое, что говорит Адам Кац , и вы оба ошибаетесь.