Почему мой счетчик Bash reset после цикла while

У меня есть Bash script, где я хочу подсчитать, сколько всего было сделано при переходе через файл. Счетчик, кажется, работает внутри цикла, но после него переменная кажется reset.

nKeys=0
cat afile | while read -r line
do
  #...do stuff
  let nKeys=nKeys+1
  # this will print 1,2,..., etc as expected
  echo Done entry $nKeys
done
# PROBLEM: this always prints "... 0 keys"
echo Finished writing $destFile, $nKeys keys

Вышеуказанный результат - это что-то одно: строки

Done entry 1
Done entry 2
Finished writing /blah, 0 keys

Выход, который я хочу:

Done entry 1
Done entry 2
Finished writing /blah, 2 keys

Я не совсем уверен, почему nKeys 0 после цикла:( Я предполагаю, что это что-то основное, но проклятое, если я могу это заметить, несмотря на просмотр http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-7.html и другие ресурсы.

Пальцы скрестились, кто-то другой может посмотреть на него и пойти "ну, а ты..."

Ответы

Ответ 1

В только что выпущенном Bash 4.2 вы можете сделать это, чтобы предотвратить создание подоболочки:

shopt -s lastpipe

Кроме того, как вы, вероятно, увидите по предоставленной Игнасио ссылке, у вас есть Бесполезное использование cat.

while read -r line
do
    ...
done < afile

Ответ 2

Как упоминалось в принятом ответе, это происходит потому, что в каналах появляются отдельные подпроцессы. Чтобы избежать этого, command grouping был лучшим вариантом для меня. То есть, делая все после трубы в подоболочке.

nKeys=0
cat afile | 
{
  while read -r line
  do
    #...do stuff
    let nKeys=nKeys+1
    # this will print 1,2,..., etc as expected
    echo Done entry $nKeys
  done
  # PROBLEM: this always prints "... 0 keys"
  echo Finished writing $destFile, $nKeys keys
}

Теперь он будет правильно сообщать значение $nKeys "(то есть, что вы пожелаете).

Ответ 3

Я пришел к желаемому результату следующим образом, не используя трубы или здесь документы


#!/bin/sh
counter=0
string="apple orange mango egg indian"
str_len=${#string}
while [ $str_len -ne 0 ]
do
   c=${string:0:1}
   if  [[ "$c" = [aeiou] ]]
   then
       echo -n "vowel : "
       echo "- $c"
       counter=$(( $counter + 1 ))
   fi
   string=${string:1}
   str_len=${#string}
done
   printf "The number of vowels in the given string are : %s "$counter
   echo