Shell variables set inside while loop not visible outside of it

后端 未结 4 1113
抹茶落季
抹茶落季 2020-11-28 06:02

I am trying to find the pathname with the most characters in it. There might be better ways to do this. But I would like to know why this problem occurs.

LO         


        
相关标签:
4条回答
  • 2020-11-28 06:31

    Do what you always (should) do:

    • separate concerns,
    • avoid globals,
    • document your code,
    • be readable,
    • maybe be POSIXy.

    (Yes, I added a bit more "good practice" ingredients to the soup than absolutely necessary ;))

    So my favorite "knee-jerk reaction" to problems with invisible subshell is to use function:

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

    Aside from avoiding the subshell annoyance, it gives you perfect place to document the code, and sort-of adds namespacing: notice that inside function you can use much shorter and simpler variable names without losing any of readability.

    0 讨论(0)
  • 2020-11-28 06:42

    The "correct" reply is given by Dennis. However, I find the process substitution trick extremely unreadable if the loop contains more than a few lines. When reading a script, I want to see what goes into the pipe before I see how it is processed.

    So I usually prefer this trick of encapsulating the while loop in "{}".

    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
    }
    
    0 讨论(0)
  • 2020-11-28 06:42

    About finding the longest pathname. Here's an alternative:

    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
    

    Forgive me if this is considered off topic, I hope it helps someone.

    0 讨论(0)
  • 2020-11-28 06:51

    When you pipe into a while loop in Bash, it creates a subshell. When the subshell exits, all variables return to their previous values (which may be null or unset). This can be prevented by using process substitution.

    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
    
    0 讨论(0)
提交回复
热议问题