Упоримся? А давай!Открывай терминал с башем и запускай команду:
while :; do cat /etc/hosts ; done | grep -m1 'a'
По идее тут что-то вроде бесконечного цикла, который будет
cat’ить на экран файл
hosts, предварительно грепнув содержимое по символу «a».
Аргумент -m1 = выход после первого вхождения искомой подстроки. После этого, конец канала закрывается для чтения.
Запустил? И всё зависло! По крайней мере у меня вывелась только одна строчка вида:
127.0.0.1 localhost bashdays
Но почему оно зависло? А потому, что утилита
cat при попытке записать данные в сломанный канал, завершается сигналом.
✔️
Давай убедимся:set -o pipefail
cat /etc/hosts | exit 0
echo $(( $? > 128 ? $? - 128 : $? ))
Получаем цифру
13. Откуда она взялась? Смотри! Если команда завершается сигналом, тогда статус ошибки
($?) == 128 + Номер сигнала.А дальше вычитаем, делим, умножаем, корень квадратный вычисляем, заводим в дискриминант по синусу. Пеню. Короче получили цифру 13.Более подробно про сигналы мы с тобой разбирали
в этом посте.Давай посмотрим, что такое
13:
kill -l 13
Вывелось
PIPE, что равносильно
SIGPIPE (Broken Pipe). Ага!
Получается что наш бесконечный цикл постоянно запускает утилиту
cat, которая тут же завершается.
Раньше эта ситуация была очевидна и на экран выводилась ошибка. А вот с версии
Bash 3.1-alpha1 это пофиксили, вот выжимка из спеков:
This document details the changes between this version, bash-3.1-alpha1,
and the previous version, bash-3.0-release.
By default, the shell no longer reports processes dying from SIGPIPE.
Нахуй так сделали, непонятно, скорее всего ушли в сторону
set pipefail, типа кому надо тот включит, чтобы лишнее гавно на экран не сыпалось.
Ладно. После запуска этого бесконечного цикла, можешь запустить
htop и визуально глянуть как порождается бесконечное количество процессов, растет счетчик
PID. А как только выработан лимит
PID, пиды начинают использоваться повторно. В общем это хорошо видно.
Ну либо так можно глянуть:
ps --no-headers --sort=pid -eo pid
Увидишь большую разницу между значениями
PID.
Теперь как это пофиксить. Если уж захотелось упороться, нужно упарываться правильно.
Вот несколько вариантов:
while : ; do cat /etc/hosts || { echo $? >&2 ; exit; } ;done | grep -m1 'a'
while : ; do cat /etc/hosts || break ; done | grep -m1 'a'
while : ; do cat /etc/hosts || exit ; done | grep -m1 'a'Если присмотреться, может возникнуть вопрос —
а откуда выходит exit? Цикл выполняется в подоболочке, потому что является участником канала.
Давай проверим:while : ; do cat ; done | grep -m1 'a' &
Запускаем конвейер в фоне. Команда
cat без указания аргументов будет читать стандартный ввод, который в данной ситуации ассоциирован с терминалом и ждет ввода данных.
А такое не позволительно фоновым процессам, поэтому такие задания будут приостановлены.
Проверяем, что оно действительно приостановлено:
jobs -l
[1]+251913 Stopped (tty input) while :; do
cat;
done
251921 | grep --color=auto -m1 'a'
Ну и теперь смотрим, кто есть кто:
ps --forest -o pid,ppid,stat,comm
PID PPID STAT COMMAND
251893 251854 Ss bash
251913 251893 T \_bash
251924 251913 T | \_cat
251921 251893 T \_grep
251962 251893 R+ \_ps
Процесс
251913/251893 и есть подоболочка, в котором крутится наш цикл. Вот с этой подоболочки и выходит exit.
Сложно? Да не… азы с некоторыми нюансами. Изучай!
tags: #linux #bash—
🔔
@bashdays