web-dev-qa-db-fra.com

L'incrément de compteur dans la boucle Bash ne fonctionne pas

J'ai le script simple suivant où je lance une boucle et que je veux maintenir un COUNTER. Je suis incapable de comprendre pourquoi le compteur ne se met pas à jour. Est-ce dû au sous-shell qui est créé? Comment puis-je potentiellement résoudre ce problème?

#!/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

Tout d'abord, vous n'augmentez pas le compteur. Remplacer COUNTER=$((COUNTER)) par COUNTER=$((COUNTER + 1)) ou COUNTER=$[COUNTER + 1] l’augmentera.

Deuxièmement, il est plus difficile de rétrodiffuser les variables de sous-shell vers l'appelé, comme vous le supposez. Les variables d'un sous-shell ne sont pas disponibles en dehors du sous-shell. Ce sont des variables locales au processus enfant.

Une façon de le résoudre consiste à utiliser un fichier temporaire pour stocker la valeur intermédiaire:

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

BASH TESTÉ: Centos, SuSE, RH

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

est une construction assez maladroite dans la programmation moderne.

(( COUNTER++ ))

semble plus "moderne". Vous pouvez aussi utiliser

let COUNTER++

si vous pensez que cela améliore la lisibilité. Parfois, Bash donne trop de façons de faire les choses - je suppose que la philosophie Perl - peut-être que le Python "il n’ya qu’une bonne façon de le faire" pourrait être plus approprié. C'est une déclaration discutable s'il en est une! Quoi qu'il en soit, je dirais que l'objectif (dans ce cas) n'est pas simplement d'incrémenter une variable, mais (règle générale) d'écrire également du code que quelqu'un d'autre peut comprendre et supporter. La conformité contribue grandement à atteindre cet objectif.

HTH

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

Essayez d'utiliser

COUNTER=$((COUNTER+1))

au lieu de

COUNTER=$((COUNTER))
13
dbf

Je pense que cet appel awk est équivalent à votre pipeline grep|grep|awk|awk: testez-le. Votre dernière commande awk semble ne rien changer du tout.

Le problème avec COUNTER est que la boucle while s'exécute dans un sous-shell, de sorte que toute modification apportée à la variable disparaît à la fermeture du sous-shell. Vous devez accéder à la valeur de COUNTER dans ce même sous-shell. Vous pouvez également suivre les conseils de @ DennisWilliamson, utiliser un processus de substitution et éviter complètement le sous-shell.

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

Au lieu d'utiliser un fichier temporaire, vous pouvez éviter de créer un sous-shell autour de la boucle while en utilisant la substitution de processus.

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

Soit dit en passant, vous devriez être capable de transformer tout ce grep, grep, awk, awk, awk en un seul awk.

À partir de Bash 4.2, il existe une option lastpipe qui

exécute la dernière commande d'un pipeline dans le contexte Shell actuel. L'option lastpipe n'a aucun effet si le contrôle du travail est activé.

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

minimaliste

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

C'est tout ce que vous devez faire:

$((COUNTER++))

Voici un extrait de Apprentissage du shell bash , 3e édition, p. 147, 148:

bash les expressions arithmétiques sont équivalentes à leurs équivalents dans les langages Java et C. [9] La priorité et l’associativité sont les mêmes que dans C. Le tableau 6-2 indique les opérateurs arithmétiques pris en charge. Bien que certains d’entre eux soient (ou contiennent) des caractères spéciaux, il n’est pas nécessaire de les échapper par une barre oblique inversée, car ils sont dans la syntaxe $ ((...)).

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

Les opérateurs ++ et - sont utiles lorsque vous souhaitez incrémenter ou décrémenter une valeur de un. [11] Ils fonctionnent de la même manière que dans Java et C, par exemple, value ++ incrémente la valeur de 1. Cela s'appelle post-incrémentation ; il existe également une valeur pré-incrémentée : ++ . La différence devient évidente avec un exemple:

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

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

4
C.E. Montijo

Il semble que vous n'ayez pas mis à jour le counter est le script, utilisez counter++

0
yjshen