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

103

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

Searene
источник
20
По крайней мере, в эти Google-ориентированные времена трудно найти этих операторов на основе символов. Есть ли поисковая система, где вы могли бы подключить «<< <<< <<» и получить что-нибудь полезное?
Даниэль Гриском
11
@DanielGriscom есть SymbolHound .
Деннис
1
@DanielGriscom Stack Exchange раньше поддерживал поиск символов, но потом что-то сломалось, и никто так и не починил.
Муру
Он уже есть (и существует уже почти год): каковы операторы управления и перенаправления оболочки?
Скотт

Ответы:

116

Здесь документ

<<известен как 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 в интерактивном режиме.

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

$ ls -l /proc/self/fd/ <<< "TEST"
total 0
lr-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/4
lrwx------ 1 user1 user1 64 Aug 20 13:43 2 -> /dev/pts/4
lr-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 +++

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

Процесс замены

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

Подстановка процессов направляет выходные данные процесса (или процессов) в стандартный поток ввода другого процесса.

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

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

Что произойдет, если мы просто заменим процесс?

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

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

Очень простой пример - сделать подстановку процесса из двух команд echo в wc:

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

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

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

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

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

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

diff <(ls /bin) <(ls /usr/bin)
Сергей Колодяжный
источник
7
< <используется, когда кто-то получает стандартный вывод из процесса подстановки . Такая команда может выглядеть следующим образом : cmd1 < <(cmd2). Например,wc < <(date)
John1024
4
Да, подстановка процессов поддерживается bash, zsh и AT & T ksh {88,93}, но не pdksh / mksh.
John1024
2
< < Само по себе не вещь , в случае замены процесса это просто <сопровождается чем-то еще, что начинается с<
user253751
1
@muru Насколько я знаю, <<<сначала был реализован порт Unix оболочки Plan 9 rc, а затем принят zsh, bash и ksh93. Я бы тогда не назвал это башизмом.
июля
3
Другой пример , где трубопровод не может быть использован: echo 'foo' | read; echo ${REPLY}будет не возвращаться foo, потому что readначинается в суб-оболочки - трубопровод начинает подоболочку. Тем не менее, read < <(echo 'foo'); echo ${REPLY}правильно возвращается foo, потому что нет вложенной оболочки.
Пэдди Ландау
26

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

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

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

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

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

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

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

Мур
источник
по твоему ответу я понял, что <<() полезнее, чем <(), верно?
solfish
1
@solfish <()дает имя, похожее на имя файла, поэтому оно более полезно - < <()заменяет стандартный ввод, когда это может не понадобиться. В wcпоследнем случае оказывается более полезным. Это может быть менее полезным в других местах
Муру
12

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

command2 | command1

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

jlliagre
источник
11

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

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

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

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

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

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

Процесс замещения и трубы

шпионить
источник