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

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

4 ответа

Решение

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

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

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

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

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

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

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

Таким образом, в действительности это похоже на передачу stdout одной команды в другую, например 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

Итак, здесь мы делаем shell создаем файловый дескриптор для всего вывода, который происходит в круглых скобках, и перенаправляем его в качестве ввода в wc Как и ожидалось, 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)

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

$ 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 $line
done< <(ls)

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

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

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

Другие вопросы по тегам