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

Mar*_*ark 41 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
Run Code Online (Sandbox Code Playgroud)

它总会以某种方式返回:

0 :
Run Code Online (Sandbox Code Playgroud)

如果我在while循环内打印调试结果,则值是正确的.那么为什么bash不能使这些变量全局化呢?

Pau*_*ce. 76

当您while在Bash中管道循环时,它会创建一个子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
Run Code Online (Sandbox Code Playgroud)

  • @Robert:问题被标记为[bash].确实,POSIX没有指定进程替换.但是,除非关注POSIX-only shell的可移植性,否则没有理由不使用特定于Bash的功能.顺便说一句,ksh和zsh支持进程替换.但是,它们不会创建导致变量丢失的子shell.另请注意,Bash 4.2有一个选项,`shopt -s lastpipe`,它"在当前shell上下文中运行管道的最后一个命令"(不是子shell) - 除非作业控制有效. (2认同)

miv*_*ivk 20

丹尼斯给出了"正确"的答复.但是,如果循环包含多行,我发现进程替换技巧极不可读.在阅读脚本时,我希望在看到它的处理方式之前看到管道中的内容.

所以我通常更喜欢在"{}"中封装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
}
Run Code Online (Sandbox Code Playgroud)

  • 除了这个例子不修改全局变量.如果你在循环后检查LONGEST_CNT**,你会发现它仍然为零 (2认同)