2014-10-01 2 views
0

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

TOTAL=0; 
cat $DATAFILE | while read LINE; 
do 
     COUNT=`echo $LINE| awk '{print $2;}'`; 
     TOTAL=$((TOTAL+COUNT)); 
done 
echo "Total = $TOTAL"; 

Однако я всегда получаю вывод «Всего = 0». Удивительно, если я переместил последнюю строку внутри цикла while, я получаю правильный результат. Например, если входной файл содержит

A 5 
B 3 
C 6 

Я получаю выход

Total = 5 
Total = 8 
Total = 14 

Но текущая версия всегда выдает 0. Кажется, значение присваивается переменной TOTAL как-то теряется.

Может ли кто-нибудь помочь мне решить проблему?

Заранее спасибо

+1

Поскольку правая сторона трубопровода выходит, когда труба завершена, если у вас нет 'shopt -s lastpipe'. –

+2

[Я устанавливаю переменные в цикле, который находится в конвейере. Почему они исчезают после завершения цикла? Или, почему я не могу передать данные для чтения?] (Http://mywiki.wooledge.org/BashFAQ/024) – fedorqui

ответ

5

Это BashFAQ #24. К счастью, вы просто нажимаете его здесь из-за ненужного использования cat - у вас нет веских оснований для использования конвейера.


Правая сторона трубопровода (как и остальная часть его содержания, составлена ​​из переходных подоболочек) выходит, когда труба будет сделана, если вы не имеете shopt -s lastpipe активные.

Вместо либо добавьте строку:

shopt -s lastpipe 

или реструктурировать свой цикл:

while read LINE; 
do 
     COUNT=`echo $LINE| awk '{print $2;}'`; 
     TOTAL=$((TOTAL+COUNT)); 
done <"$DATAFILE" 

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

while read LINE; 
do 
     COUNT=`echo $LINE| awk '{print $2;}'`; 
     TOTAL=$((TOTAL+COUNT)); 
done < <(cat "$DATAFILE") 

Кстати, если ваша sheba нг является #!/bin/bash, не #!/bin/sh, это было бы лучше записать следующим образом:

total=0 
while read -r _ count do; 
    ((total += count)) 
done <"$DATAFILE" 

read можно разделить строки на символы в самой IFS - вам не нужно использовать awk для этого.

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

+3

Важная информация здесь состоит в том, что каждая часть конвейера выполняется в подоболочке. Это означает, что цикл while while выполняется в другом BASH, который завершает -> все переменные умирают вместе с ним. –