Приращение переменной в bash

рассмотрим следующий скрипт:

#!/bin/bash

num=0
cat file | while read line; do
    echo "$line"
    lines[$num]="$line"
    ((num++))
    echo "num = $num"
done

echo "end num = $num"

i=0
while [ $i -lt $num ]; do
    echo "${lines[$i]}"
    ((i++))
done

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

dsfkljhhsdfsdfshdjkfgd
num = 1
fdfgdfgdfg
num = 2
dfgdfgdfgdfg
num = 3
dfgdfgdfgdfgdfg
num = 4
dfgdfgdfgdfgdfgd
num = 5
fgdfgdfgdfg
num = 6
dfgdfgdfgdfg
num = 7
dfgdfgdfgdfgdfg
num = 8
dfgdfgdfgdfg
num = 9
dfgdfgdgdgdg
num = 10
dfgdffgdgdgdg
num = 11
end num = 0

почему это? Как добиться запоминания переменной? Я использую bash 3.1.17 на SUSE Linux 10.

3 ответов


почему? Потому что это:

cat file | while read line; do
    echo "$line"
    lines[$num]="$line"
    ((num++))
    echo "num = $num"
done

работает while оператор в отдельном процессе, со своей собственной средой, не касаясь родительской среды. Вы также обнаружите, что lines выбора не было.

следующий упрощенный скрипт показывает это в действии:

#!/bin/bash
export xyzzy=42
echo urk | while read line; do
    xyzzy=999
    echo $xyzzy
done
echo $xyzzy

вывод этого скрипта:

999
42

потому что установка переменной 999 сделано в подпроцесс.

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

если вы используете перенаправление ввода, а не запуск конвейера подпроцессов, он должен работать так, как вы хотите. Вот ведь while бит затем выполняется в контексте настоящее процесс, а не отдельное процесс в конвейере. Например:

#!/bin/bash
export xyzzy=42
while read line; do
    xyzzy=999
    echo $xyzzy
done <<EOF
hello
EOF
echo $xyzzy

будет:

999
999

для вашего конкретного случая, заменить:

done <<EOF
hello
EOF

С:

done <file

чтобы добавить к предыдущему ответу: и чтобы избежать этого (и UUOC), вам понадобится что-то вроде этого:

while ...; do
  ...
done < file

просто держите свои переменные все в одной среде. На линии 4, удалить "кошка " файл"|". В строке 9 добавьте "