У чому різниця між <<, <<<і < <в Баш?
У чому різниця між <<, <<<і < <в Баш?
Відповіді:
Ось документ
<<відома як 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редагувати в циклі.
<()використовується для процесу заміщення. Додаткову інформацію та приклад <()можна знайти за цим посиланням: