A variable modified inside a while loop is not remembered

后端 未结 8 2201
挽巷
挽巷 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: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))
    

提交回复
热议问题