Чому використання "так" на башевих конвеєрах * не * викликає нескінченні петлі?


16

Відповідно до його документації, bash чекає, поки всі команди в трубопроводі закінчаться, перш ніж продовжувати

Оболонка чекає, коли всі команди в трубопроводі завершаться перед поверненням значення.

То чому команда yes | trueзакінчується негайно? Чи не повинен yesцикл назавжди і змусити трубопровід ніколи не повернутися?


І під питання: відповідно до специфікації POSIX , конвеєрні трубопроводи можуть вибрати або повернутися після завершення останньої команди, або чекати, поки всі команди завершаться. Чи мають у цьому сенсі звичайні оболонки різної поведінки? Чи є снаряди, де yes | trueназавжди будуть петлі?


yes | tee >(true) >/dev/nullзробить так, як ви очікуєте, btw, як teeтриває, поки всі письменники не мертві, тому trueвихід із системи не порушить його повністю.
Чарльз Даффі

1
trueв основному це {return 0;}програма, тому я не очікував, що вона буде працювати довго, не кажучи вже назавжди.
Дмитро Григор’єв

Відповіді:


33

При trueвиході зчитувана сторона труби закрита, але yesпродовжує намагатися записати на сторону запису. Ця умова називається "розбита труба", і це змушує ядро ​​надсилати SIGPIPEсигнал yes. Оскільки yesнічого особливого не стосується цього сигналу, він буде вбитий. Якщо він проігнорував сигнал, його writeвиклик не вдасться з кодом помилки EPIPE. Програми, які це роблять, повинні бути готові помітити EPIPEі припинити писати, інакше вони перейдуть у нескінченний цикл.

Якщо ви робите strace yes | true1, ви можете бачити ядро, що готується до обох можливостей:

write(1, "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"..., 4096) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=17556, si_uid=1000} ---
+++ killed by SIGPIPE +++

straceпереглядає події через API налагодження, який спочатку повідомляє про системний виклик, який повертається з помилкою, а потім про сигнал. З yesточки зору, однак, сигнал відбувається спочатку. (Технічно сигнал подається після того, як ядро ​​повертає управління в простір користувача, але перед тим, як виконувати будь-які інші інструкції на машині, тому writeфункція "обгортка" в бібліотеці C не отримує шансів встановити errnoі повернутися до програми.)


1 На жаль, straceспецифічно для Linux. У більшості сучасних Unixes є якась команда, яка робить щось подібне, але вона часто має іншу назву, вона, ймовірно, не розшифровує аргументи системного виклику так ретельно, а іноді працює лише для root.


3
@hugomg в цьому випадку труба абсолютно не має значення.
муру

3
@hugomg, тому що в трубці нічого yesне пов'язано.
муру

4
Це правда, що це демонстрація документально підтвердженої поведінки "зачекайте, поки всі команди закінчаться, перш ніж закінчити конвеєр". Це просто заважає yesотримати SIGPIPE, оскільки FD, на який він пише, не підключений до труби.
Том Хант

2
@hugomg, це циклічно назавжди так само, yes >/dev/nullяк і назавжди. Це взагалі нічого не демонструє про трубопроводи, що також не стосується простих команд (як поведінка в очікуванні на припинення, як вказує Том, стосується і простих команд).
Чарльз Даффі

2
@zwol: Я думаю, що ми використовуємо тут наші терміни з дещо різними значеннями або думаємо про речі з дещо іншого погляду ... але в будь-якому випадку write()(функція в libc) не повертається (передача управління на ПК, що слідує за нею), поки після обробник сигналу запущений, але оскільки обробник сигналу припиняє програму, управління ніколи не передається і тому write()ніколи не повертається. Так, це реалізовано в ядрі, маючи певну xxx_write()функцію повернення -EPIPE, але ми налагоджуємо програму в просторі користувачів і не зацікавлені в цьому.
Дітріх Епп

5

Чи є снаряди, де так | правда буде петля назавжди?

Навряд чи, оскільки yesкоманда використовує трубу, і вона не вийде, коли труба розірвана. sleepз іншого боку, не використовує трубу, тому:

sleep 100000000 | true

буде працювати щонайменше 100000000 секунд.


2
Будьте уважні до всіх сучасних оболонок, які не розщеплюються для останньої (правої) вбудованої команди в трубі та де trueвбудований. Це відноситься і до останніх версій Bourne Shell, ksh93, zsh. Якщо ви натиснете, ^Zколи виконується така команда, це призупинить сон і оболонка ніколи не зможе відновитись без сторонньої допомоги.
schily

3
zsh 4.3.4 (i386-pc-solaris2.11) тут, тому здається, це було недавно змінено. Цікава ідея, мені потрібно розібратися, чи зможу я реалізувати подібне виправлення для Bourne Shell. Все ще залишається питання, як це працює, і яка група процесів використовується, як в оболонці Борна, той факт, що найправіша команда є вбудованим, виявляється після того, як група процесів для сну вже була створена назавжди.
schily

2
@CharlesDuffy з того, що я розумію, schily підтримує версію sh, до якої він підтримує вдосконалення від сучасних оболонок. Він розмістив про це тут, десь.
муру

3
Оболонка Борна в архівах реліквії зберігалася до ~ 2007 року, але ніколи не робилася повністю портативною, оскільки вона все ще містить дзвінки sbrk(). Портативна та підтримувана версія знаходиться в пакеті інструментів schily, і @Charles Duffy вже виявив місце для інформації ;-)
schily

2
@muru багато функцій, які я підтримав до оболонки Борна, - це від мого bsh(Бертольд Шелл від VBERTOS, версія з покращеною віртуальною пам'яттю UNOS - перший клон UNIX). Bsh отримав багато функцій csh у 1984 та 1985 роках, але механізм псевдоніму від UNOS був перевершений в порівнянні з механізмом csh у 1980 році. Інші нові функції Bourne Shell отримані від POSIX, щоб наблизити його до POSIX.
шилі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.