web-dev-qa-db-ja.com

Bashループ内のカウンターの増分が機能しない

ループを実行していて、COUNTERを維持したい場合は、次のような単純なスクリプトがあります。カウンターが更新されていない理由がわかりません。それはサブシェルのthatsが作成されることによるものですか?どうすればこれを修正できますか?

#!/bin/bash

WFY_PATH=/var/log/nginx
WFY_FILE=error.log
COUNTER=0
grep 'GET /log_' $WFY_PATH/$WFY_FILE | grep 'upstream timed out' | awk -F ', ' '{print $2,$4,$0}' | awk '{print "http://domain.com"$5"&ip="$2"&date="$7"&time="$8"&end=1"}' | awk -F '&end=1' '{print $1"&end=1"}' |
(
while read WFY_URL
do
    echo $WFY_URL #Some more action
    COUNTER=$((COUNTER+1))
done
)

echo $COUNTER # output = 0
111
Sparsh Gupta

まず、あなたはカウンターを増やしていません。 COUNTER=$((COUNTER))COUNTER=$((COUNTER + 1))またはCOUNTER=$[COUNTER + 1]に変更すると増加します。

第二に、あなたが推測するようにサブシェル変数を呼び出し先に逆伝播することはトリッキーです。サブシェル内の変数はサブシェル外では使用できません。これらは、子プロセスにとってローカルな変数です。

これを解決する1つの方法は、中間値を格納するための一時ファイルを使用することです。

TEMPFILE=/tmp/$$.tmp
echo 0 > $TEMPFILE

# Loop goes here
  # Fetch the value and increase it
  COUNTER=$[$(cat $TEMPFILE) + 1]

  # Store the new value
  echo $COUNTER > $TEMPFILE

# Loop done, script done, delete the file
unlink $TEMPFILE
151
bos
COUNTER=1
while [ Your != "done" ]
do
     echo " $COUNTER "
     COUNTER=$[$COUNTER +1]
done

テスト済みBASH:Centos、SuSE、RH

83
Jay Stan
COUNTER=$((COUNTER+1)) 

現代のプログラミングではかなり不器用な構成です。

(( COUNTER++ ))

もっと "モダン"に見えます。また使用することができます

let COUNTER++

読みやすさが向上すると思われる場合時々、Bashはあまりにも多くのやり方でやり方をします - 私が思うにPerlの哲学 - おそらくPythonが「それをするためのただ一つの正しい方法がある」がより適切かもしれないとき。それがあったとしても、それは議論の余地のある声明です!とにかく、(この場合の)目的は単に変数を増やすことではなく、他の誰かが理解してサポートできるコードを書くことであることをお勧めします。適合性は、それを達成するのに大いに役立ちます。

HTH

36
Bill Parker
count=0   
base=1
(( count += base ))
13
pkm

使ってみる

COUNTER=$((COUNTER+1))

の代わりに

COUNTER=$((COUNTER))
12
dbf

この単一のawk呼び出しはあなたのgrep|grep|awk|awkパイプラインと同等であると思います。それをテストしてください。あなたの最後のawkコマンドは何も変わらないようです。

COUNTERの問題点は、whileループがサブシェル内で実行されているため、サブシェルが終了すると変数への変更がすべて失われることです。同じサブシェルでCOUNTERの値にアクセスする必要があります。または@ DennisWilliamsonのアドバイスを受けて、プロセスの置き換えを使用し、サブシェルを完全に避けてください。

awk '
  /GET \/log_/ && /upstream timed out/ {
    split($0, a, ", ")
    split(a[2] FS a[4] FS $0, b)
    print "http://example.com" b[5] "&ip=" b[2] "&date=" b[7] "&time=" b[8] "&end=1"
  }
' | {
    while read WFY_URL
    do
        echo $WFY_URL #Some more action
        (( COUNTER++ ))
    done
    echo $COUNTER
}
10
glenn jackman

一時ファイルを使用する代わりに、プロセス置換を使用してwhileループの周りにサブシェルを作成することを避けることができます。

while ...
do
   ...
done < <(grep ...)

ところで、そのgrep, grep, awk, awk, awkをすべて単一のawkに変換できるはずです。

Bash 4.2以降では、以下のようなlastpipeオプションがあります。

現在のシェルコンテキストでパイプラインの最後のコマンドを実行します。ジョブ制御が有効になっている場合、lastpipeオプションは効果がありません。

bash -c 'echo foo | while read -r s; do c=3; done; echo "$c"'

bash -c 'shopt -s lastpipe; echo foo | while read -r s; do c=3; done; echo "$c"'
3
8

ミニマリスト

counter=0
((counter++))
echo $counter
5
geekzspot

これがあなたがする必要があるすべてです:

$((COUNTER++))

これはbashシェルの学習、第3版、147、148ページからの抜粋です。

bash算術式は、JavaおよびC言語の対応するものと同等です。優先順位と結合性はCと同じです。表6-2に、サポートされている算術演算子を示します。これらの中には特殊文字(または含む)もありますが、それらは$((...))構文内にあるため、円記号をエスケープする必要はありません。

..........................

++および - 演算子は、値を1つ増減する場合に便利です。[11]これらはJavaやCと同じように動作します。例えば、value++はvalueを1つ増加させます。これはと呼ばれます。インクリメント後; プリインクリメント:++もあります。違いは例で明らかになります。

$ i=0
$ echo $i
0
$ echo $((i++))
0
$ echo $i
1
$ echo $((++i))
2
$ echo $i
2

http://www.safaribooksonline.com/a/learning-the-bash/7572399/ を参照してください。

4
C.E. Montijo

counterがスクリプトであることを更新しなかったようです。counter++を使用してください

0
yjshen