Shell变量设置在内部,而循环在其外部不可见

时间:2011-01-12 10:08:38

标签: bash

我正在尝试找到其中包含最多字符的路径名。可能有更好的方法来做到这一点。但我想知道为什么会出现这个问题。

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 个答案:

答案 0 :(得分:74)

当您在Bash中输入while循环时,它会创建一个子shell。子shell退出时,所有变量都返回其先前的值(可以为null或未设置)。这可以通过使用进程替换来防止。

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

答案 1 :(得分:20)

“正确”回复由Dennis给出。但是,如果循环包含多行,我发现进程替换技巧极不可读。在阅读脚本时,我希望在看到它如何处理之前看到管道中的内容。

所以我通常更喜欢在“{}”中封装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 :(得分:2)

关于找到最长的路径名。这是另一种选择:

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

请原谅我,如果这被认为是偏离主题,我希望它可以帮助某人。

答案 3 :(得分:1)

做你应该做的事情:

  • 单独关注,
  • 避免全局,
  • 记录您的代码,
  • 可读,
  • 也许是POSIXy。

(是的,我添加了更多的&#34;良好的做法&#34;汤的成分超过绝对必要;)

所以我最喜欢的&#34;膝跳反应&#34;对隐形子shell的问题是使用函数:

#!/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'"

除了避免子shell烦恼之外,它还为您提供了记录代码的完美位置,并且排序添加了命名空间:请注意,在函数内部,您可以使用更短更简单的变量名,而不会丢失任何可读性。