A variable modified inside a while loop is not remembered

后端 未结 8 2176
挽巷
挽巷 2020-11-21 05:06

In the following program, if I set the variable $foo to the value 1 inside the first if statement, it works in the sense that its value is remember

相关标签:
8条回答
  • 2020-11-21 05:32

    How about a very simple method

        +call your while loop in a function 
         - set your value inside (nonsense, but shows the example)
         - return your value inside 
        +capture your value outside
        +set outside
        +display outside
    
    
        #!/bin/bash
        # set -e
        # set -u
        # No idea why you need this, not using here
    
        foo=0
        bar="hello"
    
        if [[ "$bar" == "hello" ]]
        then
            foo=1
            echo "Setting  \$foo to $foo"
        fi
    
        echo "Variable \$foo after if statement: $foo"
    
        lines="first line\nsecond line\nthird line"
    
        function my_while_loop
        {
    
        echo -e $lines | while read line
        do
            if [[ "$line" == "second line" ]]
            then
            foo=2; return 2;
            echo "Variable \$foo updated to $foo inside if inside while loop"
            fi
    
            echo -e $lines | while read line
    do
        if [[ "$line" == "second line" ]]
        then
        foo=2;          
        echo "Variable \$foo updated to $foo inside if inside while loop"
        return 2;
        fi
    
        # Code below won't be executed since we returned from function in 'if' statement
        # We aready reported the $foo var beint set to 2 anyway
        echo "Value of \$foo in while loop body: $foo"
    
    done
    }
    
        my_while_loop; foo="$?"
    
        echo "Variable \$foo after while loop: $foo"
    
    
        Output:
        Setting  $foo 1
        Variable $foo after if statement: 1
        Value of $foo in while loop body: 1
        Variable $foo after while loop: 2
    
        bash --version
    
        GNU bash, version 3.2.51(1)-release (x86_64-apple-darwin13)
        Copyright (C) 2007 Free Software Foundation, Inc.
    
    0 讨论(0)
  • 2020-11-21 05:35

    You are the 742342nd user to ask this bash FAQ. The answer also describes the general case of variables set in subshells created by pipes:

    E4) If I pipe the output of a command into read variable, why doesn't the output show up in $variable when the read command finishes?

    This has to do with the parent-child relationship between Unix processes. It affects all commands run in pipelines, not just simple calls to read. For example, piping a command's output into a while loop that repeatedly calls read will result in the same behavior.

    Each element of a pipeline, even a builtin or shell function, runs in a separate process, a child of the shell running the pipeline. A subprocess cannot affect its parent's environment. When the read command sets the variable to the input, that variable is set only in the subshell, not the parent shell. When the subshell exits, the value of the variable is lost.

    Many pipelines that end with read variable can be converted into command substitutions, which will capture the output of a specified command. The output can then be assigned to a variable:

    grep ^gnu /usr/lib/news/active | wc -l | read ngroup
    

    can be converted into

    ngroup=$(grep ^gnu /usr/lib/news/active | wc -l)
    

    This does not, unfortunately, work to split the text among multiple variables, as read does when given multiple variable arguments. If you need to do this, you can either use the command substitution above to read the output into a variable and chop up the variable using the bash pattern removal expansion operators or use some variant of the following approach.

    Say /usr/local/bin/ipaddr is the following shell script:

    #! /bin/sh
    host `hostname` | awk '/address/ {print $NF}'
    

    Instead of using

    /usr/local/bin/ipaddr | read A B C D
    

    to break the local machine's IP address into separate octets, use

    OIFS="$IFS"
    IFS=.
    set -- $(/usr/local/bin/ipaddr)
    IFS="$OIFS"
    A="$1" B="$2" C="$3" D="$4"
    

    Beware, however, that this will change the shell's positional parameters. If you need them, you should save them before doing this.

    This is the general approach -- in most cases you will not need to set $IFS to a different value.

    Some other user-supplied alternatives include:

    read A B C D << HERE
        $(IFS=.; echo $(/usr/local/bin/ipaddr))
    HERE
    

    and, where process substitution is available,

    read A B C D < <(IFS=.; echo $(/usr/local/bin/ipaddr))
    
    0 讨论(0)
提交回复
热议问题