Перейти к содержанию

Подводные камни в Bash №7

Продолжаем делать правильно.

Этот код выглядит вполне нормально:

grep foo bar.txt | while read -r; do ((count++)); done

Он считает, сколько строк в файле bar.txt содержат слово foo.

Здесь главная проблема — переменная count не изменится вне цикла while, потому что в Bash каждая команда в пайплайне (|) запускается в отдельной оболочке (subshell). То есть count++ происходит «внутри», и снаружи этого не видно.

Если простым языком: Каждая часть, разделённая |, запускается в отдельной «коробке» (subshell). То есть while работает внутри своей коробки. B всё что там происходит, не видно снаружи.

Некоторые оболочки ksh93 или Bash с включённой настройкой shopt -s lastpipe работают по-другому — цикл выполняется в той же оболочке, и тогда count изменится.

count=0
echo -e "one\ntwo\nthree" | while read line; do ((count++)); done
echo $count

Этот код вернет 0, несмотря на count++, а вот например в zsh вернется 3.

Как быть?

Первый вариант:

shopt -s lastpipe

Эта штука говорит интерпретатору — сделай всё сам и не передавай работу другому.

Второй вариант:

Вообще избавиться от while и всё сделать через grep:

count=$(grep -c foo bar)
echo $count

Можно еще наколхозить и передавать значения через временный файл, но это прям пиздец шляпа и костыль.

Выводы

Нужно просто посчитать строки:

count=$(grep -c foo bar)

Нужно обрабатывать строки:

while read line; do ...; done < bar

Хочешь использовать pipe:

shopt -s lastpipe

Вот и вся наука.

ㅤЧитать первым в Телеграм

Комментарии