В чем разница между <<, <<< и < < в bash?

В чем разница между <<, <<< и < < в баш?

Вот документ

<< известен как here-document структура. Вы даете программе знать, каким будет конечный текст, и всякий раз, когда этот разделитель будет виден, программа будет считывать весь материал, который вы передали программе в качестве входных данных, и выполнять над ним задачу.

Вот что я имею в виду:

$ wc << EOF> one two three> four five> EOF 2  5 24

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

В bash они реализуются с помощью временных файлов, обычно в виде /tmp/sh-thd.<random string>, в то время как в dash они реализованы как анонимные каналы. Это можно наблюдать с помощью отслеживания системных вызовов с помощью strace команда. Заменять bash с sh чтобы увидеть, как /bin/sh выполняет это перенаправление.

$ strace -e open,dup2,pipe,write -f bash -c 'cat <<EOF> test> EOF'

Здесь строка

<<< известен как here-string. Вместо того, чтобы вводить текст, вы передаете программе предварительно созданную строку текста. Например, с такой программой, как bc мы можем сделать bc <<< 5*4 чтобы просто получить выходные данные для этого конкретного случая, нет необходимости запускать bc в интерактивном режиме. Думайте об этом как об эквиваленте echo '5*4' | bc.

Here-строки в bash реализуются с помощью временных файлов, обычно в формате /tmp/sh-thd.<random string>, которые позже отсоединяются, что приводит к тому, что они временно занимают некоторое пространство в памяти, но не отображаются в списке /tmp записи каталога и фактически существуют как анонимные файлы, на которые все еще может ссылаться через дескриптор файла сама оболочка, и этот дескриптор файла наследуется командой и позже дублируется в дескриптор файла 0 (stdin) через dup2() функция. Это можно наблюдать с помощью

$ ls -l /proc/self/fd/ <<< "TEST"total 0lr-x------ 1 user1 user1 64 Aug 20 13:43 0 -> /tmp/sh-thd.761Lj9 (deleted)lrwx------ 1 user1 user1 64 Aug 20 13:43 1 -> /dev/pts/4lrwx------ 1 user1 user1 64 Aug 20 13:43 2 -> /dev/pts/4lr-x------ 1 user1 user1 64 Aug 20 13:43 3 -> /proc/10068/fd

И с помощью трассировки системных вызовов (выходные данные сокращены для удобства чтения; обратите внимание, что временный файл открывается как fd 3, в него записываются данные, затем он повторно открывается с помощью O_RDONLY отметьте как fd 4 и более поздние несвязанные, затем dup2() на fd 0, который наследуется cat позже ):

$ strace -f -e open,read,write,dup2,unlink,execve bash -c 'cat <<< "TEST"'execve("/bin/bash", ["bash", "-c", "cat <<< \"TEST\""], [/* 47 vars */]) = 0...strace: Process 10229 attached[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDWR|O_CREAT|O_EXCL, 0600) = 3[pid 10229] write(3, "TEST", 4)         = 4[pid 10229] write(3, "\n", 1)           = 1[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDONLY) = 4[pid 10229] unlink("/tmp/sh-thd.uhpSrD") = 0[pid 10229] dup2(4, 0)                  = 0[pid 10229] execve("/bin/cat", ["cat"], [/* 47 vars */]) = 0...[pid 10229] read(0, "TEST\n", 131072)   = 5[pid 10229] write(1, "TEST\n", 5TEST)       = 5[pid 10229] read(0, "", 131072)         = 0[pid 10229] +++ exited with 0 +++--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=10229, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---+++ exited with 0 +++

Мнение: потенциально, поскольку строки here используют временные текстовые файлы, это возможная причина, по которой строки here всегда вставляют завершающую новую строку, поскольку текстовый файл по Определение POSIX должны быть строки, заканчивающиеся символом новой строки.

Замена процесса

Как tldp.org объясняет,

Замена процесса передает выходные данные процесса (или процессов) в stdin другого процесса.

Таким образом, по сути, это похоже на трубопровод stdout из одной команды в другую , например echo foobar barfoo | wc . Но обратите внимание: в страница bash manpage вы увидите, что он обозначается как <(list). Таким образом, в принципе, вы можете перенаправить вывод нескольких (!) команд.

Примечание: технически, когда вы говорите < < вы имеете в виду не что-то одно, а два перенаправления с одним < и процесс перенаправления выходных данных из <( . . .).

Теперь, что произойдет, если мы просто обработаем замену?

$ echo <(echo bar)/dev/fd/63

Как вы можете видеть, оболочка создает временный файловый дескриптор /dev/fd/63 куда идет вывод (который в соответствии с Ответ Жиля, является анонимным каналом). Это означает < перенаправляет этот файловый дескриптор в качестве входных данных в команду.

Таким образом, очень простым примером было бы сделать процесс подстановки выходных данных из двух команд echo в wc:

$ wc < <(echo bar;echo foo)      2       2       8

Итак, здесь мы заставляем оболочку создавать дескриптор файла для всех выходных данных, которые отображаются в круглых скобках, и перенаправлять их в качестве входных данных в wc .Как и ожидалось, wc получает этот поток от двух команд echo, которые сами по себе выводят две строки, каждая из которых содержит слово, и соответственно у нас есть 2 слова, 2 строки и 6 символов плюс две новые строки.

Как осуществляется замена процесса? Мы можем выяснить это, используя приведенную ниже трассировку (выходные данные сокращены для краткости).

$ strace -e clone,execve,pipe,dup2 -f bash -c 'cat <(/bin/true) <(/bin/false) <(/bin/echo)'execve("/bin/bash", ["bash", "-c", "cat <(/bin/true) <(/bin/false) <"...], 0x7ffcb96004f8 /* 50 vars */) = 0pipe([3, 4])                            = 0dup2(3, 63)                             = 63clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f6c12569a10) = 8954strace: Process 8954 attached[pid  8953] pipe([3, 4])                = 0[pid  8953] dup2(3, 62)                 = 62[pid  8953] clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f6c12569a10) = 8955strace: Process 8955 attached[pid  8953] pipe([3, 4])                = 0[pid  8953] dup2(3, 61)                 = 61[pid  8953] clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f6c12569a10) = 8956[pid  8953] execve("/bin/cat", ["cat", "/dev/fd/63", "/dev/fd/62", "/dev/fd/61"], 0x55ab7566e8f0 /* 50 vars */) = 0

Приведенная выше трассировка в Ubuntu (которая также подразумевается в Linux в целом) предполагает, что замена процессов реализуется путем многократного разветвления нескольких подпроцессов (так, процесс 8953 разветвляет несколько дочерних процессов 8954,8955,856 и т.д.). Затем все эти подпроцессы обмениваются данными через свой stdout , но это дублируется (то есть копируется) в стек следующих доступных файловых дескрипторов, начиная с 63 и ниже. Зачем начинать с 63 лет? Это может быть хорошим вопросом для разработчиков. Достоверно известно, что bash можно использовать fd 255 для сохранения файловых дескрипторов для команды/конвейера "main" при перенаправлении его потоков.

Боковое примечание: Замена процесса может быть упомянута как башизм (команда или структура, используемая в продвинутых оболочках, таких как bash, но не указано POSIX), но это было реализовано в ksh до существования bash как справочная страница ksh и этот ответ предлагать.Раковины, подобные tcsh и mksh однако у них нет замены процесса. Итак, как мы могли бы обойти перенаправление вывода нескольких команд в другую команду без замены процесса? Группировка плюс трубопроводы!

$ (echo foo;echo bar) | wc      2       2       8

Фактически это то же самое, что и в приведенном выше примере, однако под капотом это отличается от замены процесса, поскольку мы делаем stdout из всей подоболочки и stdin из wc соединенный с трубой. С другой стороны, подстановка процесса заставляет команду считывать временный файловый дескриптор.

Итак, если мы можем выполнять группировку с помощью трубопроводов, зачем нам нужна замена процесса? Потому что иногда мы не можем использовать трубопроводы. Рассмотрим приведенный ниже пример - сравнение выходных данных двух команд с diff (для которого требуется два файла, и в этом случае мы даем ему два файловых дескриптора)

diff <(ls /bin) <(ls /usr/bin)

< < это синтаксическая ошибка:

$ cat < <bash: syntax error near unexpected token `<'

< <() является замена процесса (<()) в сочетании с перенаправлением (<):

Надуманный пример:

$ wc -l < <(grep ntfs /etc/fstab)4$ wc -l <(grep ntfs /etc/fstab)4 /dev/fd/63

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

Чтобы было ясно, нет никакого < < оператор.

< < это синтаксическая ошибка, вы, вероятно, имеете в виду command1 < <( command2 ) который представляет собой простое перенаправление входных данных с последующей заменой процесса и очень похож, но не эквивалентен :

command2 | command1

Разница в том, что вы работаете bash является command1 выполняется в подоболочке во втором случае, в то время как в первом случае выполняется в текущей оболочке. Это означает, что переменные, установленные в command1 не было бы потеряно при использовании варианта замены процесса.

< < выдаст синтаксическую ошибку. Правильное использование заключается в следующем:

Объяснение с помощью примеров:

Пример для < <():

while read line;do   echo $linedone< <(ls)

В приведенном выше примере входные данные для цикла while будут поступать из ls команда, которая может быть прочитана построчно и echoэд в курсе событий.

<() используется для замены процесса. Дополнительная информация и пример для <() можно найти по этой ссылке:

Замена технологического процесса и трубы

По крайней мере, в наше время, ориентированное на Google, трудно искать эти операторы на основе символов. Есть ли поисковая система, где вы могли бы подключить “<< <<<< <” и получить что-нибудь полезное?

@DanielGriscom Там [SymbolHound](http://symbolhound.com /).

@DanielGriscom Stack Exchange раньше поддерживал поиск символов, но потом что-то сломалось, и никто так и не исправил это.

@muru классический обмен стеками

Это уже есть (и существует уже почти год): Каковы операторы управления и перенаправления оболочки?