Канал - это файл, открытый в файловой системе ядра, и он недоступен как обычный файл на диске. Он автоматически буферизуется только до определенного размера и в конечном итоге блокируется при заполнении. В отличие от файлов, получаемых на блочных устройствах, каналы ведут себя очень похоже на символьные устройства и, как правило, не поддерживают их, lseek()
и данные, считываемые с них, не могут быть снова прочитаны, как вы могли бы сделать с обычным файлом.
Здесь строка - это обычный файл, созданный в смонтированной файловой системе. Оболочка создает файл и сохраняет его дескриптор, одновременно удаляя его единственную ссылку на файловую систему (и, следовательно, удаляя его), прежде чем когда-либо записать / прочитать байт в / из файла. Ядро будет поддерживать пространство, необходимое для файла, пока все процессы не освободят все дескрипторы для него. Если у ребенка, читающего такой дескриптор, есть возможность сделать это, его можно перемотать lseek()
и прочитать снова.
В обоих случаях токены <<<
и |
представляют файловые дескрипторы и не обязательно сами файлы. Вы можете получить лучшее представление о том, что происходит, выполнив такие вещи, как:
readlink /dev/fd/1 | cat
...или...
ls -l <<<'' /dev/fd/*
Наиболее существенное различие между этими двумя файлами заключается в том, что здесь-строка / doc является практически единым делом - оболочка записывает все данные в него, прежде чем предложить дескриптор чтения дочернему элементу. С другой стороны, оболочка открывает канал для соответствующих дескрипторов и разветвляет дочерние элементы, чтобы управлять ими для канала - и поэтому он записывается / читается одновременно на обоих концах.
Эти различия, однако, только целом верны. Насколько я знаю (что на самом деле не так уж и далеко), это справедливо практически для каждой оболочки, которая обрабатывает <<<
сокращенную строку <<
здесь для перенаправления документа здесь, за единственным исключением yash
. yash
, busybox
, dash
, И другие ash
варианты имеют тенденцию к спине здесь-документы с трубами, хотя, и поэтому в этих снарядов там действительно очень мало разницы между ними после всего.
Хорошо - два исключения. Теперь, когда я думаю об этом, на ksh93
самом деле вообще не выполняет конвейер |
, а скорее обрабатывает весь бизнес с сокетами - хотя он и делает удаленный файл tmp, <<<*
как и большинство других. Более того, он только помещает отдельные секции конвейера в среду подоболочек, что является своего рода эвфемизмом POSIX, по крайней мере, он действует как подоболочка , и даже не делает вилки.
Дело в том, что результаты теста @ PSkocik (который очень полезен) могут здесь сильно различаться по многим причинам, и большинство из них зависят от реализации. Для настройки данного документа основными факторами будут ${TMPDIR}
тип целевой файловой системы и текущая конфигурация / доступность кэша, а также объем записываемых данных. Для канала это будет размер самого процесса оболочки, потому что копии создаются для необходимых вилок. Таким образом bash
, ужасно при настройке конвейера (включая подстановки $(
команд )
) - потому что он большой и очень медленный, но с ksh93
ним вряд ли что-то меняет.
Вот еще один небольшой фрагмент оболочки, демонстрирующий, как оболочка разделяется на подоболочки для конвейера:
pipe_who(){ echo "$$"; sh -c 'echo "$PPID"'; }
pipe_who
pipe_who | { pipe_who | cat /dev/fd/3 -; } 3<&0
32059 #bash's pid
32059 #sh's ppid
32059 #1st subshell's $$
32111 #1st subshell sh's ppid
32059 #2cd subshell's $$
32114 #2cd subshell sh's ppid
Разница между тем, что pipe_who()
сообщается в конвейерном вызове, и отчетом об одном прогоне в текущей оболочке, связана с тем, что в (
подоболочке )
указано, как запрашивать pid родительской оболочки $$
при расширении. Хотя bash
подоболочки определенно являются отдельными процессами, $$
специальный параметр оболочки не является надежным источником этой информации. Тем не менее, дочерняя sh
оболочка subshell не отказывается точно сообщать о ней $PPID
.