在while循环内修改的变量不会被记住
在下面的程序中,如果我在第一个if
语句中将变量$foo
设置为值1,它的工作原理就是它的值在if语句之后被记住。 然而,当我在while
语句内部的if
设置相同的变量值为2 while
,它在while
循环后被遗忘。 它的行为就像我在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 linensecond linenthird 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)
echo -e $lines | while read line
...
done
while
循环在子shell中执行。 因此,一旦subshell退出,对变量所做的任何更改都将不可用。
相反,您可以使用here字符串将while循环重写为主壳程序; 只有echo -e $lines
才能在子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")"
更新:#2
Blue Moon的答案就是解释。
替代方案:
消除echo
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
(消除echo
):
arr=("first line" "second line" "third line");
for((i=0;i<${#arr[*]};++i)) { line=${arr[i]};
...
}
变量$lines
可以转换为数组而不必启动新的子shell。 字符和
n
必须转换为某个字符(例如一个真实的新行字符)并使用IFS(内部字段分隔符)变量将字符串拆分为数组元素。 这可以像这样完成:
lines="first linensecond linenthird line"
echo "$lines"
OIFS="$IFS"
IFS=$'n' arr=(${lines//n/$'n'}) # Conversion
IFS="$OIFS"
echo "${arr[@]}", Length: ${#arr[*]}
set|grep ^arr
结果是
first linensecond linenthird line
first line second line third line, Length: 3
arr=([0]="first line" [1]="second line" [2]="third line")
您是742342nd用户,请问这个bash FAQ。 答案还描述了在由管道创建的子壳体中设置的变量的一般情况:
E4)如果我将命令的输出传送到read variable
,为什么当读取命令结束时输出不会显示在$variable
?
这与Unix进程之间的父子关系有关。 它会影响在管道中运行的所有命令,而不仅仅是简单的read
。 例如,将命令的输出管道输送到反复调用read
的while
循环中会导致相同的行为。
管道的每个元素,即使是内置函数或shell函数,都在单独的进程中运行,即运行管道的shell的子进程。 子流程不能影响父级的环境。 当read
命令将变量设置为输入时,该变量仅在子shell中设置,而不是在父shell中设置。 当子shell退出时,变量的值将丢失。
许多以read variable
结尾的流水线可以转换为命令替换,它将捕获指定命令的输出。 输出可以被分配给一个变量:
grep ^gnu /usr/lib/news/active | wc -l | read ngroup
可以转换成
ngroup=$(grep ^gnu /usr/lib/news/active | wc -l)
不幸的是,这并不能在多个变量之间分割文本,就像在给定多个可变参数时所做的那样。 如果您需要这样做,您可以使用上面的命令替换将输出读取到变量中,并使用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"
但是要小心,这会改变shell的位置参数。 如果你需要它们,你应该在做这些之前先保存它们。
这是一般方法 - 在大多数情况下,您不需要将$ 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))
链接地址: http://www.djcxy.com/p/25571.html
上一篇: A variable modified inside a while loop is not remembered