Почему цикл while останавливается после приостановки?

10

Почему при использовании bash и приостановке цикла while цикл возобновляется после возобновления? Краткий пример ниже.

$ while true; do echo .; sleep 1; done
.
.
^Z
[1]+  Stopped                 sleep 1
$ fg
sleep 1
$

Я знаком с сигналами, и я предполагаю, что это может быть естественным поведением bash, но я бы хотел лучше понять, почему он ведет себя именно так.

bkzland
источник
потому что он должен обрабатывать прерывание и должен точно отражать это при $?возврате, и это trueне так true. наверное. думаю.
mikeserv
1
Я надеюсь, что этот комментарий не будет помечен, но я отвечу на ваш вопрос другим вопросом, в стиле юникс-коан: «Почему ученик перестает сражаться на детской площадке после того, как его отстраняют?» Ответ таков, потому что он больше не на игровой площадке, где у него есть возможность начать драки. Таким образом, рассматриваемое поведение было просто остановлено.
Rubynorails
Вы останавливаете команду, цикл прерывается. Затем вы возобновляете одну команду сна 1, а не цикл.
123

Ответы:

10

Это похоже на ошибку в нескольких оболочках, она работает как положено с ksh93 и zsh .

Задний план:

Кажется, что большинство оболочек запускают цикл while внутри основной оболочки и

Bourne Shell приостанавливает всю оболочку, если вы наберете ^ Z с оболочкой без входа в систему

bash приостанавливает только sleepфункцию и затем покидает цикл while в пользу печати нового приглашения оболочки

тире делает эту команду невыполнимой

С ksh93 все работает совсем иначе:

ksh93 делает то же самое, в то время как команда запускается в первый раз, но, как sleepи buitin в ksh93, ksh93 имеет обработчик, который заставляет цикл while отключаться от основной оболочки, а затем приостанавливается в тот момент, когда вы вводите ^ Z.

Если вы введете ksh93 позже, дочерний элементfg , который все еще выполняет цикл, будет продолжен.

Вы видите основное отличие при сравнении сообщений jobcontrol от bash и ksh93:

Bash сообщает:

[1]+ Stopped sleep 1

но ksh93 сообщает:

^Z[1] + Stopped while true; do echo .; sleep 1; done

Zsh ведет себя подобно ksh93

В обеих оболочках у вас есть один процесс (основная оболочка), если вы не вводите ^ Z, и два процесса оболочки после ввода ^ Z.

Шили
источник
на dashсамом деле не обрабатывает сигнал, когда цикл заканчивается? в [d]?ashисходном коде все эти макросы для INTON и INTOFF разбросаны по всему, и обычно сигналы, принимаемые в состоянии INTOFF, действительно обрабатываются в (или вокруг) INTON . в любом случае, мне только любопытно, потому что я думаю, что ты знаешь лучше - это отличный ответ. Спасибо.
Микесерв
Я редко использую dash, и я недавно получил и скомпилировал его для сравнения производительности с bash, ksh93 и моей Bourne Shell. Выполняя эти тесты, я обнаружил, что тире в основном кажется быстрым, потому что он не включает поддержку многобайтовых символов. Один sleep 100может быть приостановлен и возобновлен dash, поэтому, похоже, он dashзнает о проблемах в этой команде и выборочно отключает управление заданиями.
Щили
Итак, в своих тестах вы смогли сравнять dashпроизводительность других оболочек, отбросив многобайтовую обработку? и да, dashподдерживает ли управление заданиями, но стандарт говорит, что интерактивная оболочка должна игнорировать TSTP, и выполнение цикла while в текущей оболочке на интерактивном терминале является не менее интерактивной оболочкой, чем любая другая.
mikeserv
Я не проверял это точно, но я мог видеть, что, хотя dash потребляет больше системного процессорного времени, чем ksh93 или Bourne Shell (в основном потому, что он выполняет больше вызовов fork ()), он использует меньше пользовательского процессорного времени, и это приводит к аналогичному общему количеству. Время процессора по сравнению с моей версией Bourne Shell. Из попыток сократить пользовательское процессорное время в Bourne Shell я знаю, что большую часть этого времени тратят на многобайтовые преобразования.
Щили
4

Я написал одному из соавторов Bash об этой проблеме, и вот его ответ:

Это не совсем ошибка, но это, безусловно, предостережение.

Идея заключается в том, что вы приостанавливаете процессы, которые отличаются от гранулярности, нежели команды оболочки. Когда процесс приостанавливается, он возвращается в оболочку (с ненулевым статусом, что имеет последствия, когда вы, скажем, останавливаете процесс, являющийся тестом цикла), у которого есть выбор: он может выйти из цикла или продолжить цикл оставляя остановленный процесс позади. Bash выбирает - и всегда выбирал - разрывать циклы, когда работа остановлена. Продолжение цикла - это редко то, что вы хотите.

Некоторые другие оболочки делают такие вещи, как разветвление копии оболочки, когда процесс приостанавливается из-за SIGTSTP, и остановка этого процесса. Bash никогда не делал этого - это кажется более сложным, чем гарантия выгоды - но если кто-то захочет представить этот код в виде патча, я бы посмотрел на включение изменений.

Поэтому, если кто-то хочет отправить патч, используйте адреса электронной почты, найденные на страницах руководства.

forthrin
источник