2013-12-19 3 views
22

Я пытаюсь написать небольшой скрипт, который будет считать записи в файле журнала, и я увеличиваю переменную (USCOUNTER), которую я пытаюсь использовать после завершения цикла.Инкрементная переменная Bash в цикле

Но в этот момент USCOUNTER выглядит как 0 вместо фактического значения. Любая идея, что я делаю неправильно? Благодаря!

FILE=$1 

tail -n10 mylog > $FILE 

USCOUNTER=0 

cat $FILE | while read line; do 
    country=$(echo "$line" | cut -d' ' -f1) 
    if [ "US" = "$country" ]; then 
     USCOUNTER=`expr $USCOUNTER + 1` 
     echo "US counter $USCOUNTER" 
    fi 
done 
echo "final $USCOUNTER" 

Он выводит:

US counter 1 
US counter 2 
US counter 3 
.. 
final 0 

ответ

28

Вы используете в субоболочке USCOUNTER, поэтому переменная не отображается в основной оболочке.

Вместо cat FILE | while ..., всего лишь while ... done < $FILE. Таким образом, можно избежать распространенной проблемы I set variables in a loop that's in a pipeline. Why do they disappear after the loop terminates? Or, why can't I pipe data to read?:

while read country _; do 
    if [ "US" = "$country" ]; then 
     USCOUNTER=$(expr $USCOUNTER + 1) 
     echo "US counter $USCOUNTER" 
    fi 
done < "$FILE" 

Примечание Я также заменил `` выражение с $().

Я также заменил while read line; do country=$(echo "$line" | cut -d' ' -f1) на while read country _. Это позволяет говорить об while read var1 var2 ... varN, где var1 содержит первое слово в строке, $var2 и так далее, до $varN содержит оставшееся содержимое.

+0

Обратите внимание, что вы можете использовать $ (($ USCOUNTER + 1)) для оценки выражения – geert3

+0

Спасибо @ geert3, хороший. Также '((USCOUNTER ++))', как использует решение anubhava. Я не добавляю его к моему ответу, если он несовместим с оболочкой OPs. – fedorqui

11

Вы получаете final 0, потому что ваш while loop выполняется в процессе sub (shell), и любые внесенные изменения не отражаются в текущей (родительской) оболочке.

Правильный сценарий:

while read -r country _; do 
    if [ "US" = "$country" ]; then 
     ((USCOUNTER++)) 
     echo "US counter $USCOUNTER" 
    fi 
done < "$FILE" 
18
+1

Почему не 'echo 'US counter $ ((USCOUNTER ++))" 'внутри if? –

+0

@ KrzysztofJabłoński, если вы хотите, чтобы он был эквивалентен приведенному выше коду, тогда вы должны использовать '' 'US counter $ ((++ USCOUNTER))" ''. Это очень хороший момент, вы правы! –

0

Приращение переменной может быть сделано так:

_my_counter=$[$_my_counter + 1] 

Подсчет количества вхождения шаблона в колонне может быть сделано с Grep

grep -cE "^([^ ]*){2}US" 

-c счета

([^ ]*) Для обнаружения колонии

{2} Колонны номер

US вашего шаблон

+1

Хотя этот код может ответить на вопрос, предоставив дополнительный контекст относительно _why_ и/или _how_ он отвечает , вопрос значительно улучшит его долгосрочное значение . Пожалуйста, отредактируйте свой ответ, чтобы добавить какое-то объяснение. –

+1

Название: «Инкрементная переменная Bash в цикле» Моя строка делает: «увеличивая переменную в bash» Так что я думаю, что она отвечает на часть вопроса (по крайней мере, как пример из geekzspot) – trax

+1

The ' $ [...] 'синтаксис для арифметического расширения [устарел] (http://wiki.bash-hackers.org/syntax/expansion/arith). Вместо этого используйте '$ ((...))'. Как ваш ответ, так и адрес geekzspot являются неотъемлемым аспектом вопроса OP, который не решает проблему (работающая с переменной составляющей часть кода OP работает (однако неэффективно)). Если вы хотите предложить улучшение _incidental_, не отвечая на вопрос, пожалуйста, четко укажите это. – mklement0

4

Я имел тот же переменный $ счетчика в цикле, получая потерянную проблему.

@fedorqui's answer (и некоторые другие) являются точными ответами на фактический вопрос: суб-оболочка действительно является проблемой.

Но это привело меня к другому вопросу: я не обжигающе содержимое файла ... но выход из серии труб & ... отбирает

мой erroring пример кода:

count=0 
cat /etc/hosts | head | while read line; do 
    ((count++)) 
    echo $count $line 
done 
echo $count 

и мое исправление благодаря помощи этой нити и process substitution:

count=0 
while IFS= read -r line; do 
    ((count++)) 
    echo "$count $line" 
done < <(cat /etc/hosts | head) 
echo "$count" 
+1

Приятно видеть, что мой ответ был полезен :) Обратите также внимание на то, что 'cat file | head' можно упростить до 'head file'. – fedorqui

0

с помощью следующей команды 1 строки для изменения имени многих файлов в Linux с помощью Фраза специфика:

find -type f -name '*.jpg' | rename 's/holiday/honeymoon/' 

Для всех файлов с расширением «.jpg», если они содержат строку „праздник“, заменить его на „медовый месяц“. Например, эта команда переименует файл «ourholiday001.jpg» на «ourhoneymoon001.jpg».

В этом примере также показано, как использовать команду find для отправки списка файлов (-type f) с расширением .jpg (-name '* .jpg') для переименования через канал (|). rename затем считывает его список файлов со стандартного ввода.

Смежные вопросы