Вопрос по bash, linux, shell – Увеличение счетчика в цикле Bash не работает

105

У меня есть следующий простой скрипт, где я запускаю цикл и хочу сохранитьCOUNTER, Я не могу понять, почему счетчик не обновляется. Это из-за подоболочки, которая создается? Как я могу это исправить?

<code>#!/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
</code>
Связанные с:stackoverflow.com/questions/13726764/… Gabriel Devillers

Ваш Ответ

10   ответов
0

counter это скрипт, используйтеcounter++

Извините за опечатку, я на самом деле использую ((COUNTER + 1)) в сценарии, который не работает Sparsh Gupta
10

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
}
Спасибо, последний awk в основном удалит все после end = 1 и поставит новый end = 1 в конец (чтобы в следующий раз мы могли удалить все, что будет добавлено после него). Sparsh Gupta
@SparshGupta, предыдущий awk не печатает ничего после "end = 1".
13
count=0   
base=1
(( count += base ))
35
COUNTER=$((COUNTER+1)) 

(( COUNTER++ ))

выглядит более "современным". Вы также можете использовать

let COUNTER++

если вы думаете, что улучшает читабельность. Иногда Bash предлагает слишком много способов сделать что-то, я полагаю, философию Perl, когда, возможно, у Python есть только один правильный способ сделать это ». может быть более подходящим. Это & APOS; S спорного заявления, если когда-либо было один! В любом случае, я хотел бы предложить цель (в данном случае) не просто увеличить переменную, а (общее правило) также написать код, который кто-то может понять и поддержать. Соответствие имеет большое значение для достижения этого.

НТН

3

Это все, что вам нужно сделать:

$((COUNTER++))

Вот выдержка изLearning the bash Shell, 3-е издание, с. 147, 148:

bash arithmetic expressions are equivalent to their counterparts in the Java and C languages.[9] Precedence and associativity are the same as in C. Table 6-2 shows the arithmetic operators that are supported. Although some of these are (or contain) special characters, there is no need to backslash-escape them, because they are within the $((...)) syntax.

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

The ++ and - operators are useful when you want to increment or decrement a value by one.[11] They work the same as in Java and C, e.g., value++ increments value by 1. This is called post-increment; there is also a pre-increment: ++value. The difference becomes evident with an example:

$ 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/

Это версия этого мне нужна, потому что я использовал его в состоянииif заявление:if [[ $((needsComma++)) -gt 0 ]]; then printf ',\n'; fi  Правильно или нет, это единственная версия, которая работает надежно.
В этой форме важно то, что вы можете использовать приращение за один шаг.i=1; while true; do echo $((i++)); sleep .1; done
8

болочки вокругwhile цикл с помощью подстановки процесса.

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

Кстати, вы должны быть в состоянии преобразовать все этоgrep, grep, awk, awk, awk в одинawk.

Начиная с Bash 4.2, естьlastpipe вариант, который

runs the last command of a pipeline in the current shell context. The lastpipe option has no effect if job control is enabled.

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
@chrisweb: я добавил информацию оlastpipe, Кстати, вы, вероятно, должны использовать"${PIPESTATUS[@]}" (на вместо звездочки).
Подстановка процесса хороша, если вы хотите увеличить счетчик внутри цикла и использовать его снаружи, когда закончите, проблема с подстановками процесса заключается в том, что я не нашел способа также получить код состояния выполненной команды, что возможно при использовании канала используя $ {PIPESTATUS [*]}
149

COUNTER=$((COUNTER)) вCOUNTER=$((COUNTER + 1)) или жеCOUNTER=$[COUNTER + 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
$ [...] устарела.
@chepner У вас есть ссылка, которая говорит$[...] устарела? Есть ли альтернативное решение?
@blong Вот SO вопрос по $ [...] против $ ((...)), в котором обсуждается и упоминается об устаревании:stackoverflow.com/questions/2415724/…
$[...] был использованbash до$((...)) был принят оболочкой POSIX. Я не уверен, что это когда-либо формально устарело, но я не могу найти упоминания об этом вbash man-страница, и кажется, что она поддерживается только для обратной совместимости.
Кроме того, $ (...) предпочтительнее, чем...
80
COUNTER=1
while [ Your != "done" ]
do
     echo " $COUNTER "
     COUNTER=$[$COUNTER +1]
done

вопросы были некоторое время с конвейером, поэтому при создании подоболочки ваш ответ верен, но вы не используете конвейер, поэтому он не отвечает на вопрос
Для меня на Ubuntu выдает ошибку: +1]: не найдено
@kroonwijk должен быть пробел перед квадратной скобкой (чтобы «разграничить слова», формально говоря). Bash не может видеть конец предыдущего выражения.
5

минималист

counter=0
((counter++))
echo $counter
11

Попробуй использовать

COUNTER=$((COUNTER+1))

вместо

COUNTER=$((COUNTER))
Я не уверен, почему, но я вижу, что мой скрипт несколько раз терпел неудачу при использовании(( COUNTER++ )) но когда я перешел наCOUNTER=$((COUNTER + 1)) это сработало.GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
или простоlet "COUNTER++"
@AaronDigulla:(( COUNTER++ )) (без знака доллара)
Извините, это была опечатка. Это на самом деле ((COUNTER + 1)) Sparsh Gupta
или просто$(( COUNTER ++ ))

Похожие вопросы