Bash变量范围


104

请向我解释为什么最后echo一句话是空白的?我希望XCODE在while循环中将其增加到1:

#!/bin/bash
OUTPUT="name1 ip ip status" # normally output of another command with multi line output

if [ -z "$OUTPUT" ]
then
        echo "Status WARN: No messages from SMcli"
        exit $STATE_WARNING
else
        echo "$OUTPUT"|while read NAME IP1 IP2 STATUS
        do
                if [ "$STATUS" != "Optimal" ]
                then
                        echo "CRIT: $NAME - $STATUS"
                        echo $((++XCODE))
                else
                        echo "OK: $NAME - $STATUS"
                fi
        done
fi

echo $XCODE

我尝试使用以下语句代替++XCODE方法

XCODE=`expr $XCODE + 1`

并且它也不会在while语句之外打印。我认为我在这里缺少有关可变范围的信息,但是联机帮助页没有向我显示。


您在哪里将XCODE初始化为可以递增的值?
Paul Tomblin

我试图在while语句之外的代码顶部抛出“ XCODE = 0”
Matt P

没有多余的东西,它对我有用。#!/ bin / bash for in 1 2 3 4 5; 做echo $((++ XCODE))做echo“ fin:” $ XCODE我认为您的问题与变量作用域无关,并且与一段时间内发生的一切有关。
Paul Tomblin

同意..好像与“ while read”循环有关?
Matt P

关于此有一个Bash常见问题解答:mywiki.wooledge.org/BashFAQ/024
法比奥说,请莫妮卡(Monica)

Answers:


117

因为您正在管道进入while循环,所以将创建一个子外壳来运行while循环。

现在,此子进程具有自己的环境副本,无法将任何变量传递回其父级(就像在任何Unix进程中一样)。

因此,您将需要进行重组,以免流到循环中。或者,您可以在一个函数中运行,例如,echo您想要从子过程中返回的值。

http://tldp.org/LDP/abs/html/subshel​​ls.html#SUBSHELL


2
这只是回答了我使用bash脚本遇到的许多看似随机的问题。
Daniel Agans

这个完美的答案让我非常不高兴,并解释了我们CI系统中一种非常奇怪的行为。
KayCee'4

108

问题在于,与管道放在一起的进程在子外壳程序中执行(因此具有自己的环境)。while管内发生的任何事情都不会影响管道外部的任何内容。

您的具体示例可以通过将管道重写为

while ... do ... done <<< "$OUTPUT"

也许

while ... do ... done < <(echo "$OUTPUT")

32
对于那些正在关注整个<()语法是什么的人(就像我以前一样),它被称为“ Process Substitution”,并且上面详述的特定用法可以在这里看到:mywiki.wooledge.org/ProcessSubstitution
罗斯·艾肯

2
每个人都应该定期使用流程替换!超级有用。我vimdiff <(grep WARN log.1 | sort | uniq) <(grep WARN log.2 | sort | uniq)每天都在做类似的事情。考虑到您可以一次使用多个文件,并将它们像文件一样对待...可能性!
布鲁诺·布鲁诺斯基

8

这应该也可以工作(因为echo和while在同一子shell中):

#!/bin/bash
cat /tmp/randomFile | (while read line
do
    LINE="$LINE $line"
done && echo $LINE )

3

另一种选择:

#!/bin/bash
cat /some/file | while read line
do
  var="abc"
  echo $var | xsel -i -p  # redirect stdin to the X primary selection
done
var=$(xsel -o -p)  # redirect back to stdout
echo $var

编辑:在这里,xsel是一个要求(安装它)。另外,您可以使用xclip: xclip -i -selection clipboard 代替 xsel -i -p


我收到错误消息:./scraper.sh:第111行:xsel:命令未找到./scraper.sh:第114行:xsel:命令未找到
3kstc

@ 3kstc显然,安装xsel。另外,您可以使用xclip,但用法有所不同。这里的要点:首先将输出放入剪贴板(Linux中有3个),第二个-从那里抓取并发送到stdout。
Rammix

1
 #!/bin/bash
 OUTPUT="name1 ip ip status"
+export XCODE=0;
 if [ -z "$OUTPUT" ]
----

                     echo "CRIT: $NAME - $STATUS"
-                    echo $((++XCODE))
+                    export XCODE=$(( $XCODE + 1 ))
             else

echo $XCODE

看看这些改变是否有帮助


这样做时,我现在得到“ 0”以打印最后一个echo语句。但是我希望该值为1而不是零。还有,为什么要使用出口?我认为这会迫使它进入环境吗?
Matt P

0

另一个选择是将结果从子Shell输出到文件中,然后在父Shell中读取它。就像是

#!/bin/bash
EXPORTFILE=/tmp/exportfile${RANDOM}
cat /tmp/randomFile | while read line
do
    LINE="$LINE $line"
    echo $LINE > $EXPORTFILE
done
LINE=$(cat $EXPORTFILE)

0

当我制作自己的小玩意儿时,我解决了这个问题:

ls -l | sed '/total/d ; s/  */\t/g' | cut -f 5 | 
( SUM=0; while read SIZE; do SUM=$(($SUM+$SIZE)); done; echo "$(($SUM/1024/1024/1024))GB" )

关键是我用()制作了一个子外壳,其中包含我的SUM变量和while,但是我将管道传递给整个()而不是while本身,这避免了问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.