Переменные оболочки, находящиеся внутри цикла, не видны вне его
Я пытаюсь найти путь с наибольшим количеством символов в нем. Там могут быть лучшие способы сделать это. Но я хотел бы знать, почему эта проблема возникает.
LONGEST_CNT=0
find samples/ | while read line
do
line_length=$(echo $line | wc -m)
if [[ $line_length -gt $LONGEST_CNT ]]
then
LONGEST_CNT=$line_length
LONGEST_STR=$line
fi
done
echo $LONGEST_CNT : $LONGEST_STR
Он как-то всегда возвращает:
0 :
Если я распечатываю результаты для отладки внутри цикла while, значения верны. Итак, почему bash не делает эти переменные глобальными?
Ответы
Ответ 1
Когда вы подключаетесь к циклу while
в Bash, он создает подоболочку. Когда подоболочка выходит, все переменные возвращаются к своим предыдущим значениям (которые могут быть нулевыми или неустановленными). Это может быть предотвращено с помощью замены процесса.
LONGEST_CNT=0
while read -r line
do
line_length=${#line}
if (( line_length > LONGEST_CNT ))
then
LONGEST_CNT=$line_length
LONGEST_STR=$line
fi
done < <(find samples/ ) # process substitution
echo $LONGEST_CNT : $LONGEST_STR
Ответ 2
"Правильный" ответ задается Dennis. Тем не менее, я считаю, что подстановка подстановки процесса чрезвычайно нечитаема, если цикл содержит более нескольких строк. При чтении script, я хочу посмотреть, что происходит в трубе, прежде чем я увижу, как он обрабатывается.
Поэтому я обычно предпочитаю этот трюк инкапсуляции цикла while в "{}".
LONGEST_CNT=0
find /usr/share/zoneinfo | \
{ while read -r line
do
line_length=${#line}
if (( line_length > LONGEST_CNT ))
then
LONGEST_CNT=$line_length
LONGEST_STR=$line
fi
done
echo $LONGEST_CNT : $LONGEST_STR
}
Ответ 3
Об обнаружении самого длинного пути. Вот альтернатива:
find /usr/share/zoneinfo | while read line; do
echo ${#line} $line
done | sort -nr | head -n 1
# Result:
58 /usr/share/zoneinfo/right/America/Argentina/ComodRivadavia
Простите меня, если это считается не по теме, я надеюсь, что это поможет кому-то.
Ответ 4
Делайте то, что вы всегда (должны) делать:
- отдельные проблемы,
- избегать глобальных привязок,
- запишите свой код,
- можно читать,
- может быть POSIXy.
(Да, я добавил немного добавочных ингредиентов к супу, чем это абсолютно необходимо;))
Итак, моя любимая реакция "колено-рывок" на проблемы с невидимой подоболочкой заключается в использовании функции:
#!/bin/sh
longest() {
#
# Print length and body of the longest line in STDIN
#
local cur_ln # current line
local cur_sz # current size (line length)
local max_sz # greatest size so far
local winner # longest string so far
max_sz=0
while read -r cur_ln
do
cur_sz=${#cur_ln}
if test "$cur_sz" -gt "$max_sz";
then
max_sz=$cur_sz
winner=$cur_ln
fi
done
echo "$max_sz" : "$winner"
}
find /usr/share/zoneinfo | longest
# ok, if you really wish to use globals, here you go ;)
LONGEST_CNT=0
LONGEST_CNT=$(
find /usr/share/zoneinfo \
| longest \
| cut -d: -f1 \
| xargs echo\
)
echo "LONGEST_CNT='$LONGEST_CNT'"
Кроме того, чтобы избежать раздражения подоболочки, он дает вам идеальное место для документирования кода и сортировки добавляет пространство имен: обратите внимание, что внутри функции вы можете использовать гораздо более короткие и простые имена переменных, не теряя при этом удобочитаемости.