Яка різниця між <<, <<< та <<у басі?


102

У чому різниця між <<, <<<і < <в Баш?


20
Принаймні, у ці Google орієнтовані часи важко шукати цих операторів на основі символів. Чи є пошукова система, де ви можете підключити "<< <<< <<" і отримати щось корисне?
Даніель Гріском

11
@DanielGriscom Є SymbolHound .
Денніс

1
@DanielGriscom Stack Exchange використовувався для підтримки пошукових символів, але потім щось зламалося, і ніхто його ніколи не виправляв.
muru

Відповіді:


115

Ось документ

<<відома як 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)

7
< <використовується, коли отримують stdin від заміщення процесу . Така команда може виглядати наступним чином : cmd1 < <(cmd2). Наприклад,wc < <(date)
John1024

4
Так, підміна процесу підтримується bash, zsh та AT&T ksh {88,93}, але не pdksh / mksh.
John1024

2
< < - це не сама річ , у разі заміни процесу - це лише <<
наступне

1
@muru Наскільки я знаю, <<<спочатку він був реалізований портом Unix оболонки Plan 9 rc, а згодом прийнятий zsh, bash та ksh93. Я б не назвав це башизмом.
jlliagre

3
Інший приклад , де трубопровід не може бути використаний: echo 'foo' | read; echo ${REPLY}буде НЕ повертатися foo, тому що readпочинається в суб-оболонки - трубопровід починає подоболочкі. Однак read < <(echo 'foo'); echo ${REPLY}правильно повертається foo, тому що немає підрозділу.
Paddy Landau

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

При підстановці процесу шлях до дескриптора файлу використовується як ім'я файлу. Якщо ви не хочете (або не можете) використовувати ім’я файлу безпосередньо, ви поєднуєте підстановку процесу з перенаправленням.

Щоб було зрозуміло, < <оператора немає .


я отримую Вашою відповіддю, що <<() корисніше, ніж <() правда?
солянка

1
@solfish <()надає річ, подібну до імені файлу, тому загалом корисніше - < <()це заміна stdin там, де це може не знадобитися. В wc, останнє трапляється бути більш корисним. Можливо, це буде менш корисно в інших місцях
muru

12

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

command2 | command1

Різниця, припускаючи, що ти працюєш bash, command1запускається в нижній частині корпусу у другому випадку, тоді як вона працює в поточній оболонці в першому. Це означає, що змінні, встановлені в command1, не втрачаються при варіанті заміщення процесу.


11

< <дасть синтаксичну помилку. Правильне використання полягає в наступному:

Пояснення за допомогою прикладів:

Приклад для < <():

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

У наведеному вище прикладі вхід до циклу while буде надходити з lsкоманди, яку можна читати рядок за рядком і echoредагувати в циклі.

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.