У чому різниця між <<
, <<<
і < <
в Баш?
У чому різниця між <<
, <<<
і < <
в Баш?
Відповіді:
Ось документ
<<
відома як here-document
структура. Ви повідомляєте програмі про те, що буде кінцевим текстом, і щоразу, коли цей роздільник буде показаний, програма прочитає всі матеріали, які ви дали програмі, як вхідні дані, і виконає завдання над нею.
Ось що я маю на увазі:
$ wc << EOF
> one two three
> four five
> EOF
2 5 24
У цьому прикладі ми кажемо wc
програмі почекати EOF
рядок, потім вводимо п'ять слів, а потім вводимо, EOF
щоб сигналізувати про те, що ми зробили введення. Насправді це схоже на біг wc
сам по собі, набираючи слова, потім натискаючиCtrlD
У 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 ,
Заміна процесу подає висновок процесу (або процесів) в дію іншого процесу.
Таким чином, це по суті схоже на передачу послідовностей однієї команди в іншу, наприклад echo foobar barfoo | wc
. Але зауважте: на баш-manpage ви побачите, що вона позначається як <(list)
. Тому в основному ви можете перенаправляти вихід декількох (!) Команд.
Примітка: технічно, коли ви говорите, < <
ви не маєте на увазі одне, а два перенаправлення з однократним <
та перенаправленням процесу на вихід з <( . . .)
.
Тепер, що станеться, якщо ми просто обробляємо заміну?
$ echo <(echo bar)
/dev/fd/63
Як бачите, оболонка створює тимчасовий дескриптор файлу, /dev/fd/63
куди йде вихід (що відповідно до відповіді Гілла - анонімна труба). Це означає, <
що дескриптор файлів перенаправляє як вхід в команду.
Тому дуже простим прикладом може бути процес підстановки виводу з двох команд ехо на wc:
$ wc < <(echo bar;echo foo)
2 2 8
Отже, тут ми робимо оболонку, щоб створити дескриптор файлу для всіх результатів, що трапляються в круглих дужках, і перенаправляє, що вхід до wc
. і належним чином у нас є 2 слова, 2 рядки та 6 символів плюс два нових рядки.
Побічна примітка: Підстановка процесу може називатися башизмом (команда або структура, застосована в розширених оболонках типу bash
, але не визначена POSIX), але вона була реалізована ksh
до існування bash як сторінка ksh man, і ця відповідь підказує. Раковини люблять tcsh
і mksh
однак не мають процесу заміщення. То як ми могли обійти перенаправлення виводу декількох команд в іншу команду без підстановки процесу? Групування плюс трубопроводи!
$ (echo foo;echo bar) | wc
2 2 8
Ефективно це те саме, що і в наведеному вище прикладі, однак, це відрізняється під кришкою від процесу заміщення, оскільки ми робимо прошивку всієї нижньої оболонки і stdin wc
зв'язаної з трубою . З іншого боку, підміна процесу змушує команду зчитувати дескриптор тимчасового файлу.
Отже, якщо ми можемо зробити групування з трубопроводами, навіщо нам потрібна підміна процесу? Тому що іноді ми не можемо використовувати трубопроводи. Розглянемо нижченаведений приклад - порівняння виходів двох команд з diff
(для чого потрібні два файли, і в цьому випадку ми надаємо йому два дескриптори файлів)
diff <(ls /bin) <(ls /usr/bin)
< <
використовується, коли отримують stdin від заміщення процесу . Така команда може виглядати наступним чином : 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
При підстановці процесу шлях до дескриптора файлу використовується як ім'я файлу. Якщо ви не хочете (або не можете) використовувати ім’я файлу безпосередньо, ви поєднуєте підстановку процесу з перенаправленням.
Щоб було зрозуміло, < <
оператора немає .
<()
надає річ, подібну до імені файлу, тому загалом корисніше - < <()
це заміна stdin там, де це може не знадобитися. В wc
, останнє трапляється бути більш корисним. Можливо, це буде менш корисно в інших місцях
< <
є синтаксичною помилкою, ви, мабуть, маєте на увазі, command1 < <( command2 )
що це просте перенаправлення вводу з подальшим підстановкою процесу та дуже схоже, але не еквівалентне:
command2 | command1
Різниця, припускаючи, що ти працюєш bash
, command1
запускається в нижній частині корпусу у другому випадку, тоді як вона працює в поточній оболонці в першому. Це означає, що змінні, встановлені в command1
, не втрачаються при варіанті заміщення процесу.
< <
дасть синтаксичну помилку. Правильне використання полягає в наступному:
Пояснення за допомогою прикладів:
Приклад для < <()
:
while read line;do
echo $line
done< <(ls)
У наведеному вище прикладі вхід до циклу while буде надходити з ls
команди, яку можна читати рядок за рядком і echo
редагувати в циклі.
<()
використовується для процесу заміщення. Додаткову інформацію та приклад <()
можна знайти за цим посиланням: