Глобальная переменная сбрасывается, если цикл отправляет вывод в канал

Согласно справочным страницам bash(1), когда я запускаю следующее:

set -e
x=2
echo Start $x
while [ $((x--)) -gt 0 ]; do echo Loop $x; done | cat
echo End $x

Вывод будет:

Start 2
Loop 1
Loop 0
End 2

После цикла (запускается как подоболочка) переменная x сбрасывается до 2. Но если я удалю канал, x будет обновлен:

Start 2
Loop 1
Loop 0
End -1

Мне нужно изменить x, но мне также нужна труба. Любая идея, как обойти эту проблему?


person Udi    schedule 04.06.2013    source источник
comment
Что здесь делает | cat? Делает ли это что-то значимое?   -  person devnull    schedule 04.06.2013
comment
stackoverflow.com/q/4667509/900873   -  person Kevin    schedule 04.06.2013
comment
@devnull, я думаю, это просто для иллюстрации: добавление канала к чему-либо будет означать, что цикл while выполняется в подоболочке, и любые изменения в переменной $x теряются, когда подоболочка заканчивается.   -  person glenn jackman    schedule 04.06.2013
comment
Да, настоящая петля длинная. Внутри цикла у меня есть несколько строк, например: ping -c1 Machine || STOP=YES В конце цикла я grep(1) вывожу строки icmp_seq=   -  person Udi    schedule 05.06.2013


Ответы (1)


bash всегда (по крайней мере, начиная с 4.2) запускает все не крайние правые части конвейера в подоболочке. Если значение x необходимо изменить в вызывающей оболочке, вы должны переписать свой код, чтобы избежать конвейера.

Один ужасный пример:

# If you commit to one bash feature, may as well commit to them all:
#   Arithmetic compound: (( x-- > 0 ))
#   Process substitution: > >( cat )
while (( x-- > 0 )); do echo Loop $x; done > >( cat )
person chepner    schedule 04.06.2013
comment
@udi, про подстановку процессов читайте здесь - person glenn jackman; 04.06.2013
comment
@chepner, большое спасибо! У меня была небольшая опечатка, и теперь это работает. - person Udi; 05.06.2013