当前位置: 首页 > 知识库问答 >
问题:

在while循环中修改的变量不会被记住

胡鸿志
2023-03-14

在下面的程序中,如果我在第一条if语句中将变量$foo设置为值1,那么它的工作原理是在if语句之后记住它的值。然而,当我将同一变量设置为while语句中的if语句中的值2时,它会在while循环后被遗忘。它的行为就像我在循环中使用变量$foo的某种副本,而我只修改特定的副本。下面是一个完整的测试程序:

#!/bin/bash

set -e
set -u 
foo=0
bar="hello"  
if [[ "$bar" == "hello" ]]
then
    foo=1
    echo "Setting \$foo to 1: $foo"
fi

echo "Variable \$foo after if statement: $foo"   
lines="first line\nsecond line\nthird line" 
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"
    fi
    echo "Value of \$foo in while loop body: $foo"
done

echo "Variable \$foo after while loop: $foo"

# Output:
# $ ./testbash.sh
# Setting $foo to 1: 1
# Variable $foo after if statement: 1
# Value of $foo in while loop body: 1
# Variable $foo updated to 2 inside if inside while loop
# Value of $foo in while loop body: 2
# Value of $foo in while loop body: 2
# Variable $foo after while loop: 1

# bash --version
# GNU bash, version 4.1.10(4)-release (i686-pc-cygwin)

共有3个答案

经博延
2023-03-14

您是询问此bash常见问题解答的第742342位用户。答案还描述了在管道创建的子shell中设置变量的一般情况:

E4)如果我将命令的输出通过管道传输到read变量中,为什么读取命令完成时输出不显示在$变量中?

这与Unix进程之间的父子关系有关。它会影响管道中运行的所有命令,而不仅仅是对read的简单调用。例如,将命令的输出管道传输到而重复调用read循环将导致相同的行为。

管道的每个元素,甚至是内置或shell函数,都在一个单独的进程中运行,即运行管道的shell的子进程。子进程不能影响其父进程的环境。当read命令将变量设置为输入时,该变量仅设置在子shell中,而不是父shell中。当子shell退出时,变量的值将丢失。

许多以读取变量结尾的管道可以转换为命令替换,这将捕获指定命令的输出。然后可以将输出分配给变量:

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

可以转换成

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

不幸的是,这并不能像read在给定多个变量参数时那样将文本分割到多个变量中。如果需要这样做,可以使用上面的命令替换将输出读取到变量中,并使用bash模式删除扩展操作符拆分变量,也可以使用以下方法的一些变体。

假设/usr/local/bin/ipaddr是以下shell脚本

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

而不是使用

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

要将本地计算机的IP地址拆分为单独的八位字节,请使用

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

但是,请注意,这将更改壳的位置参数。如果您需要它们,您应该在执行此操作之前保存它们。

这是一般的方法——在大多数情况下,您不需要将$IFS设置为不同的值。

其他一些用户提供的替代方案包括:

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

并且,如果可以使用流程替代,

read A B C D < <(IFS=.; echo $(/usr/local/bin/ipaddr))
柴文林
2023-03-14

已更新#2

蓝月亮的回答中有解释。

替代解决方案:

消除回显

while read line; do
...
done <<EOT
first line
second line
third line
EOT

在这里是文档中添加回声

while read line; do
...
done <<EOT
$(echo -e $lines)
EOT

在后台运行echo:

coproc echo -e $lines
while read -u ${COPROC[0]} line; do 
...
done

显式重定向到文件句柄(注意

exec 3< <(echo -e  $lines)
while read -u 3 line; do
...
done

或者只是重定向到stdin

while read line; do
...
done < <(echo -e  $lines)

和一个用于chepner(消除回显):

arr=("first line" "second line" "third line");
for((i=0;i<${#arr[*]};++i)) { line=${arr[i]}; 
...
}

变量$line可以转换为数组,而无需启动新的子shell。字符\n必须转换为某些字符(例如真正的新行字符)并使用IFS(内部字段分隔符)变量将字符串拆分为数组元素。这可以像:

lines="first line\nsecond line\nthird line"
echo "$lines"
OIFS="$IFS"
IFS=$'\n' arr=(${lines//\\n/$'\n'}) # Conversion
IFS="$OIFS"
echo "${arr[@]}", Length: ${#arr[*]}
set|grep ^arr

结果是

first line\nsecond line\nthird line
first line second line third line, Length: 3
arr=([0]="first line" [1]="second line" [2]="third line")

包和泰
2023-03-14
echo -e $lines | while read line 
    ...
done

while循环在子shell中执行。因此,一旦子shell退出,对变量所做的任何更改都将不可用。

取而代之的是,您可以使用这里的字符串重新编写while循环以在主shell进程中;只有echo-e$line将在子shell中运行:

while read line
do
    if [[ "$line" == "second line" ]]
    then
        foo=2
        echo "Variable \$foo updated to $foo inside if inside while loop"
    fi
    echo "Value of \$foo in while loop body: $foo"
done <<< "$(echo -e "$lines")"

您可以通过在分配时立即扩展反斜杠序列来摆脱上面这里字符串中相当丑陋的回显$'...'引用形式可以在那里使用:

lines=$'first line\nsecond line\nthird line'
while read line; do
    ...
done <<< "$lines"
 类似资料:
  • 问题内容: 我被困在我应该声明一个称为“ phrase”的字符串变量的部分,该变量不应一直循环播放。 让您知道我的任务是:与选项1相似,不同之处在于用户在输入第一队的结果后输入“ N”(而不是“ Q”)。然后,程序输入第二个团队名称及其结果,直到输入“ Q”。输出两个语句,例如选项1中的语句,然后输出第三条语句,该语句说明哪个团队处于第一位(基于点数) 输入样例: 样本输出: 更新 : 我的代码:

  • 问题内容: 我刚刚遇到了这段代码 并且认为, 必须 有一个比用无限循环更好的方法。 所以我尝试了: 显然有一个错误。 有什么方法可以避免在那种情况下使用? 编辑: 理想情况下,您要避免重复说两次…恕我直言,重复甚至比a还要糟糕,尤其是在语句很复杂的情况下。 问题答案: 如果您不对数据做任何奇特的事情,例如以后再阅读更多行,总会有:

  • 问题内容: 我一生无法理解为什么我无法在while循环之外阅读postPrioity。我尝试过“ export postPrioity =“ 500””仍然无法正常工作。 有任何想法吗? -或在计划文字中- 输出:(我在files.txt中只有3个文件名) 问题答案: 管道操作员创建一个子外壳,请参阅BashPitfalls和BashFAQ。解决方案:不要使用,反正毫无用处。

  • 对于我的程序,我要编写一个程序,它接受2到10之间的行数。生成n行的乘法三角形。每行包含的条目不超过其行大小。这一点我没有问题。但是,在用户将数字0输入到我的问题“请输入要打印的行数:”之后,应该终止循环并打印“感谢您使用此程序!”我用了一个DO。。。WHILE循环以确定用户是否希望继续。在我的循环中,我将用户想要打印的数字声明为int num。我的循环应该持续到num

  • 在for循环中,为不同的变量分配一个值。已经赋值的变量将从下一次迭代中获得赋值。最后,两个变量的值相同。该代码用于验证文件中的数据。当我打印这些值时,它会为第一次迭代打印正确的值,但在下一次迭代中,第一次迭代中指定的值会更改。当我在for循环中打印$value3和$value4的值时,它会为$value4显示null,为$value3显示一些值,但在下一次迭代中,$value3的值会被$value

  • 问题内容: public class GuardedBlock { 我正在通过Java Essentials教程学习并发性。找到了守卫的方块并尝试对其进行测试。我无法理解的一件事。 虽然循环是无限的,但是如果您取消注释threadMessage行,则一切正常。为什么? 问题答案: 简短答案 您忘了声明为易失性布尔值。 如果您将字段声明省略为,则不会告诉JVM该字段可以被多线程看到,例如您的示例。