Переменные оболочки, установленные внутри, пока цикл не виден снаружи - PullRequest
40 голосов
/ 12 января 2011

Я пытаюсь найти путь с наибольшим количеством символов в нем. Там могут быть лучшие способы сделать это. Но я хотел бы знать, почему эта проблема возникает.

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 не делает эти переменные глобальными?

Ответы [ 4 ]

75 голосов
/ 12 января 2011

Когда вы создаете канал в 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
20 голосов
/ 02 декабря 2012

«Правильный» ответ дает Деннис . Тем не менее, я считаю, что трюк замещения процесса чрезвычайно нечитаем, если цикл содержит более нескольких строк. При чтении сценария я хочу увидеть, что происходит в канале, прежде чем я увижу, как он обрабатывается.

Поэтому я обычно предпочитаю этот трюк инкапсуляции цикла 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
}
2 голосов
/ 27 декабря 2013

О поиске самого длинного пути. Вот альтернатива:

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

Простите, если это считается не по теме, надеюсь, это кому-нибудь поможет.

1 голос
/ 03 декабря 2015

Делайте то, что вы всегда (должны) делать:

  • отдельные проблемы,
  • избегать глобальных символов,
  • документировать свой код,
  • be
  • может быть 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'"

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...