Bash変数スコープ


104

最後のechoステートメントが空白である理由を説明してください。XCODEwhileループで値が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ステートメントの外では印刷されません。ここでは変数スコープについて何か不足していると思いますが、olのマニュアルページには表示されていません。


XCODEをインクリメントできるものにどこで初期化しますか?
ポールトンブリン2009

コードの先頭、whileステートメントの外で「XCODE = 0」をスローしようとしました
Matt P

クリフトがなければ、それは私にとってはうまくいきます。#!/ bin / bash for i in 1 2 3 4 5; do echo $((++ XCODE))done echo "fin:" $ XCODEあなたの問題は、変数のスコープや、何が起こっているのかとは関係ありません。
ポールトンブリン

同意します。「while read」ループと関係があるように見えますか?
Matt P

これについてはBash FAQがあります:mywiki.wooledge.org/BashFAQ/024
Fabioは

回答:


117

whileループにパイプしているので、whileループを実行するサブシェルが作成されます。

これで、この子プロセスには独自の環境のコピーがあり、変数を親に戻すことはできません(UNIXプロセスと同様)。

したがって、ループにパイピングしないように再構築する必要があります。あるいは、たとえば関数で実行し、echo必要な値をサブプロセスから返すこともできます。

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


2
これは、私がbashスクリプトで実行していた一見ランダムな問題の多くに答えました。
Daniel

この完璧な答えは私をとても動揺させ、CIシステムの本当に奇妙な振る舞いを説明します。
KayCee 2017

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)毎日のようにしています。一度に複数を使用してファイルのように扱うことができることを考慮してください...可能性!
Bruno Bronosky

8

これも機能するはずです(echoとwhileが同じサブシェルにあるため):

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

3

もう1つのオプション:

#!/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 -i -selection clipboard 代わりに xclip:を使用できます。 xsel -i -p


エラーが表示されます:./scraper.sh:111行目:xsel:コマンドが見つかりません./scraper.sh:114行目:xsel:コマンドが見つかりません
3kstc

@ 3kstcは明らかに、xselをインストールします。また、xclipを使用することもできますが、その使用法は少し異なります。ここでの要点:最初に出力をクリップボード(Linuxでは3つ)に配置し、2番目にそこから取得してstdoutに送信します。
Rammix 2016年

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」が表示されます。ただし、値はゼロではなく1になると思います。また、なぜ輸出を使用するのですか?私はそれを環境に強制すると思いますか?
マットP

0

別のオプションは、結果をサブシェルからファイルに出力し、それを親シェルで読み取ることです。何かのようなもの

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

0

私が自分の小さなduを作っているときに私はこれを回避しました:

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自体ではなく()全体にパイプ処理するため、問題を回避できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.