чому бэш не йде під час виходу з циклу, коли передається в підключений до підкоманди?


12

Чому команда внизу не виходить? Замість виходу цикл працює нескінченно.

Хоча я виявив таку поведінку за допомогою більш складної установки, найпростіша форма команди зводиться до наступного.

Не виходить:

while /usr/bin/true ; do echo "ok" | cat ; done | exit 1

Вище немає помилок друку. Кожен '|' - це труба. "Вихід 1" означає ще один процес, який пробіг і закінчився.

Я очікую, що "вихід 1" спричинить SIGPIPE на циклі "time" (пишіть на трубі без зчитувача) і циклічному вимикання. Але цикл продовжує працювати.

Чому команда не зупиняється?


zsh виходить нормально.
Брайам

Відповіді:


13

Це пов'язано з вибором в реалізації.

Запуск одного і того ж сценарію на Solaris з ksh93виробляє іншу поведінку:

$ while /usr/bin/true ; do echo "ok" | cat ; done | exit 1
cat: write error [Broken pipe]

Що викликає проблему - це внутрішній конвеєр, без нього цикл виходить із будь-якої оболонки / ОС:

$ while /usr/bin/true ; do echo "ok" ; done | exit 1
$

cat отримує сигнал SIGPIPE під bash, але оболонка все одно повторює цикл.

Process 5659 suspended
[pid 28801] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
[pid 28801] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28801 detached
Process 28800 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Process 28802 attached
Process 28803 attached
[pid 28803] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
Process 5659 suspended
[pid 28803] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28803 detached
Process 28802 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Process 28804 attached
Process 28805 attached (waiting for parent)
Process 28805 resumed (parent 5659 ready)
Process 5659 suspended
[pid 28805] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
[pid 28805] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28805 detached
Process 28804 detached
--- SIGCHLD (Child exited) @ 0 (0) ---

Документація Bash зазначає:

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

Документація Ksh зазначає:

Кожна команда, крім останньої, виконується як окремий процес; оболонка чекає завершення останньої команди .

POSIX повідомляє:

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


Я думаю, що не саме внутрішній конвеєр викликає проблему, це те, що вбудований echoігнорує SIGPIPE. Ви також можете відтворити проблему, використовуючи env echoзамість echo(щоб примусити використовувати фактичний echoдвійковий код). (Порівняйте також вихід { echo hi; echo $? >&2; } | exit 1і { env echo hi; echo $? >&2; } | exit 1.)
Лукас Веркмайстер

1

Це питання клопоче мене роками. Завдяки jilliagre за штовхання в потрібному напрямку.

Трохи перезапитувавши питання, у моєму вікні linux це завершено як очікувалося:

while true ; do echo "ok"; done | head

Але якщо я додам трубу, вона не закінчується, як очікувалося:

while true ; do echo "ok" | cat; done | head

Це засмучувало мене роками. Розглядаючи відповідь, написану jilliagre, я придумав це чудове виправлення:

while true ; do echo "ok" | cat || exit; done | head

QED ...

Ну, не зовсім. Ось дещо складніше:

i=0
while true; do
    i=`expr $i + 1`
    echo "$i" | grep '0$' || exit
done | head

Це не працює правильно. Я додав || exitтак, що він знає, як закінчити достроково, але найперший echoне відповідає grepтому, цикл вимикається відразу. У цьому випадку вас справді не цікавить статус виходу grep. Моя робота - додати ще одну cat. Отже, ось надуманий сценарій під назвою "десятки":

#!/bin/bash
i=0
while true; do
    i=`expr $i + 1`
    echo "$i" | grep '0$' | cat || exit
done

Це належним чином припиняється при запуску як tens | head. Дякую, Боже.

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