В чем разница между <<
, <<<
и < <
в Баш?
В чем разница между <<
, <<<
и < <
в Баш?
Ответы:
Здесь документ
<<
известен как 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)
< <
используется, когда кто-то получает стандартный вывод из процесса подстановки . Такая команда может выглядеть следующим образом : cmd1 < <(cmd2)
. Например,wc < <(date)
< <
Само по себе не вещь , в случае замены процесса это просто <
сопровождается чем-то еще, что начинается с<
<<<
сначала был реализован порт Unix оболочки Plan 9 rc, а затем принят zsh, bash и ksh93. Я бы тогда не назвал это башизмом.
echo 'foo' | read; echo ${REPLY}
будет не возвращаться foo
, потому что read
начинается в суб-оболочки - трубопровод начинает подоболочку. Тем не менее, read < <(echo 'foo'); echo ${REPLY}
правильно возвращается foo
, потому что нет вложенной оболочки.
< <
это синтаксическая ошибка:
$ cat < <
bash: syntax error near unexpected token `<'
< <()
Является ли процесс замены ( <()
) в сочетании с перенаправлением ( <
):
Придуманный пример:
$ wc -l < <(grep ntfs /etc/fstab)
4
$ wc -l <(grep ntfs /etc/fstab)
4 /dev/fd/63
При замене процесса путь к дескриптору файла используется как имя файла. Если вы не хотите (или не можете) использовать имя файла напрямую, вы объединяете подстановку процесса с перенаправлением.
Чтобы было ясно, нет < <
оператора.
<()
дает имя, похожее на имя файла, поэтому оно более полезно - < <()
заменяет стандартный ввод, когда это может не понадобиться. В wc
последнем случае оказывается более полезным. Это может быть менее полезным в других местах
< <
это синтаксическая ошибка, вы, вероятно, имеете в виду, command1 < <( command2 )
что это простое перенаправление ввода с последующей подстановкой процесса и очень похоже, но не эквивалентно:
command2 | command1
Разница в том, что вы работаете, bash
заключается command1
в том, что во втором случае он запускается в подоболочке, а в первом - в текущей оболочке. Это означает, что переменные, установленные в command1
, не будут потеряны с вариантом замены процесса.
< <
выдаст синтаксическую ошибку. Правильное использование заключается в следующем:
Объясняя с помощью примеров:
Пример для < <()
:
while read line;do
echo $line
done< <(ls)
В приведенном выше примере вход в цикл while будет поступать из ls
команды, которую можно читать построчно и echo
редактировать в цикле.
<()
используется для замены процесса. Дополнительную информацию и пример <()
можно найти по этой ссылке: