it-swarm.com.de

Zählerinkrement in der Bash-Schleife funktioniert nicht

Ich habe das folgende einfache Skript, in dem ich eine Schleife ausführe und ein COUNTER verwalten möchte. Ich kann nicht herausfinden, warum der Zähler nicht aktualisiert wird. Liegt es daran, dass eine Subshell erstellt wird? Wie kann ich das möglicherweise beheben?

#!/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
115
Sparsh Gupta

Erstens erhöhen Sie den Zähler nicht. Wenn Sie COUNTER=$((COUNTER)) in COUNTER=$((COUNTER + 1)) oder COUNTER=$[COUNTER + 1] Ändern, wird dies erhöht.

Zweitens ist es schwieriger, Subshell-Variablen an den Angerufenen zurückzusenden, wie Sie vermuten. Variablen in einer Subshell sind außerhalb der Subshell nicht verfügbar. Dies sind lokale Variablen für den untergeordneten Prozess.

Eine Möglichkeit, dies zu lösen, besteht darin, eine temporäre Datei zum Speichern des Zwischenwerts zu verwenden:

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
152
bos
COUNTER=1
while [ Your != "done" ]
do
     echo " $COUNTER "
     COUNTER=$[$COUNTER +1]
done

Getestete Bash: Centos, SuSE, RH

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

ist ein ziemlich ungeschicktes Konstrukt in der modernen Programmierung.

(( COUNTER++ ))

sieht "moderner" aus. Sie können auch verwenden

let COUNTER++

wenn Sie denken, dass dies die Lesbarkeit verbessert. Manchmal gibt Bash zu viele Möglichkeiten, um Dinge zu tun - Perl-Philosophie, nehme ich an -, wenn vielleicht das Python "es gibt nur einen richtigen Weg, es zu tun" passender ist. Das ist eine umstrittene Aussage, wenn es jemals eine gab! Jedenfalls würde ich vorschlagen, dass das Ziel (in diesem Fall) nicht nur darin besteht, eine Variable zu inkrementieren, sondern (allgemeine Regel), auch Code zu schreiben, den jemand anderes verstehen und unterstützen kann. Die Konformität trägt wesentlich dazu bei.

HTH

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

Versuchen zu benutzen

COUNTER=$((COUNTER+1))

anstatt von

COUNTER=$((COUNTER))
13
dbf

Ich denke, dieser einzelne awk-Aufruf entspricht Ihrem grep|grep|awk|awk Pipeline: Bitte testen Sie es. Ihr letzter awk-Befehl scheint überhaupt nichts zu ändern.

Das Problem bei COUNTER ist, dass die while-Schleife in einer Subshell ausgeführt wird, sodass alle Änderungen an der Variablen verschwinden, wenn die Subshell beendet wird. Sie müssen auf den Wert von COUNTER in derselben Subshell zugreifen. Oder befolgen Sie @ DennisWilliamsons Rat, verwenden Sie eine Prozessersetzung und vermeiden Sie die Unterschale insgesamt.

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
}
11
glenn jackman

Anstatt eine temporäre Datei zu verwenden, können Sie das Erstellen einer Subshell um die while -Schleife vermeiden, indem Sie die Prozessersetzung verwenden.

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

Übrigens sollten Sie in der Lage sein, all das grep, grep, awk, awk, awk In ein einziges awk umzuwandeln.

Ab Bash 4.2 gibt es die Option lastpipe

führt den letzten Befehl einer Pipeline im aktuellen Shell-Kontext aus. Die Option lastpipe hat keine Auswirkung, wenn die Auftragssteuerung aktiviert ist.

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
9

minimalistisch

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

Das ist alles was Sie tun müssen:

$((COUNTER++))

Hier ist ein Auszug aus Lernen der Bash-Shell , 3. Ausgabe, S. 147, 148:

bash arithmetische Ausdrücke entsprechen ihren Gegenstücken in den Sprachen Java und C. [9] Priorität und Assoziativität sind die Tabelle 6-2 zeigt die unterstützten arithmetischen Operatoren. Obwohl einige dieser Operatoren Sonderzeichen sind (oder Sonderzeichen enthalten), müssen sie nicht mit einem Backslash-Escape versehen werden, da sie sich innerhalb des $ ((... )) Syntax.

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

Die Operatoren ++ und - sind nützlich, wenn Sie einen Wert um eins erhöhen oder verringern möchten. [11] Sie funktionieren wie in Java und C, z. B. value ++ erhöht den Wert um 1. Dies wird aufgerufen Nachinkrement ; es gibt auch ein Vorinkrement : ++ Wert Der Unterschied wird anhand eines Beispiels deutlich:

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

Siehe http://www.safaribooksonline.com/a/learning-the-bash/7572399/

4
C.E. Montijo

Es scheint, dass Sie das Skript counter nicht aktualisiert haben. Verwenden Sie counter++

0
yjshen