Bash循环中的计数器增量无效

Spa*_*pta 117 linux bash shell scripting counter

我有以下简单的脚本,我正在运行一个循环,并希望维护一个COUNTER.我无法弄清楚为什么计数器没有更新.这是由于子shell创建了吗?我怎样才能解决这个问题?

#!/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
Run Code Online (Sandbox Code Playgroud)

bos*_*bos 155

首先,你没有增加柜台.更改COUNTER=$((COUNTER))COUNTER=$((COUNTER + 1))COUNTER=$[COUNTER + 1]将增加它.

其次,当你猜测时,将子shell变量反向传播给被调用者是很棘手的.子shell外部不提供子shell中的变量.这些是子进程的本地变量.

解决它的一种方法是使用临时文件来存储中间值:

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
Run Code Online (Sandbox Code Playgroud)

  • $ [...]已被弃用. (28认同)
  • 在$((...))被POSIX shell采用之前,`$ [...]`被`bash`使用.我不确定它是否曾被正式弃用,但我在`bash`手册页中找不到它,它似乎只支持向后兼容性. (8认同)
  • @blong这是关于$ [...] vs $((...))的SO问题,讨论并引用了弃用:http://stackoverflow.com/questions/2415724/bash-arithmetic-expression-vs-算术表达式 (5认同)

小智 85

COUNTER=1
while [ Your != "done" ]
do
     echo " $COUNTER "
     COUNTER=$[$COUNTER +1]
done
Run Code Online (Sandbox Code Playgroud)

测试BASH:Centos,SuSE,RH

  • 对我来说,在Ubuntu上给出错误:+1]:找不到 (2认同)
  • 问题是关于管道的一段时间,因此在创建子外壳的地方,您的答案是正确的,但您不使用管道,因此它没有回答问题 (2认同)
  • 根据切普纳对另一个答案的评论,“$[]”语法已被弃用。/sf/ask/736117511/#comment13600255_10516135 (2认同)
  • 这不能解决主要问题,主循环放置在子shell下 (2认同)

小智 39

COUNTER=$((COUNTER+1)) 
Run Code Online (Sandbox Code Playgroud)

在现代编程中是一个非常笨拙的结构.

(( COUNTER++ ))
Run Code Online (Sandbox Code Playgroud)

看起来更"现代".你也可以使用

let COUNTER++
Run Code Online (Sandbox Code Playgroud)

如果你认为这提高了可读性.有时,Bash提供了太多的做事方式 - 我认为Perl哲学 - 也许Python"只有一种正确的方法"可能更合适.如果有的话,这是一个值得商榷的陈述!无论如何,我建议目标(在这种情况下)不仅仅是增加变量,而是(一般规则)也编写其他人可以理解和支持的代码.符合性对实现这一点有很大帮助.

HTH

  • 这没有解决最初的问题,即如何在结束(子进程)循环后获取计数器中的更新值 (4认同)

dbf*_*dbf 13

尝试使用

COUNTER=$((COUNTER+1))
Run Code Online (Sandbox Code Playgroud)

代替

COUNTER=$((COUNTER))
Run Code Online (Sandbox Code Playgroud)

  • 或者只是`让'COUNTER ++"` (8认同)
  • @AaronDigulla:`((COUNTER ++))`(没有美元符号) (8认同)
  • 对不起,这是一个错字。它实际上 ((COUNTER+1)) (2认同)
  • 我不知道为什么,但我看到我的脚本在使用 `(( COUNTER++ ))` 时反复失败,但是当我切换到 `COUNTER=$((COUNTER + 1))` 时,它起作用了。`GNU bash,版本 4.1.2(1)-release (x86_64-redhat-linux-gnu)` (2认同)

pkm*_*pkm 13

count=0   
base=1
(( count += base ))
Run Code Online (Sandbox Code Playgroud)


gle*_*man 11

我认为这个单独的awk调用等同于你的grep|grep|awk|awk管道:请测试它.你的上一个awk命令似乎什么都没改变.

COUNTER的问题是while循环在子shell中运行,因此当subshel​​l退出时,对变量的任何更改都会消失.您需要在相同的子shell中访问COUNTER的值.或者采取@ DennisWilliamson的建议,使用流程替换,并完全避免子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
}
Run Code Online (Sandbox Code Playgroud)


Pau*_*ce. 9

您可以while通过使用进程替换来避免在循环周围创建子shell,而不是使用临时文件.

while ...
do
   ...
done < <(grep ...)
Run Code Online (Sandbox Code Playgroud)

顺便说一下,你应该能够把所有这些grep, grep, awk, awk, awk变成一个单一的awk.

从Bash 4.2开始,有一个lastpipe选项

在当前shell上下文中运行管道的最后一个命令.如果启用了作业控制,则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
Run Code Online (Sandbox Code Playgroud)


gee*_*pot 7

极简主义

counter=0
((counter++))
echo $counter
Run Code Online (Sandbox Code Playgroud)

  • 例如有问题的情况下不起作用,因为有 subshel​​l (2认同)

小智 7

有两个条件导致((var++))我的表达失败:

  1. 如果我将 bash 设置为严格模式( set -euo pipefail) 并且如果我从零(0) 开始增量。

  2. 从一 (1) 开始很好,但零会导致在评估“++”时增量返回“1”,这在严格模式下是非零返回代码失败。

我可以使用((var+=1))var=$((var+1))逃避这种行为

  • 正如 Ilkka Virta(他回答了 bug-bash@gnu.org 的问题)向我解释的那样,这不是 inc/dec 运算符,而是 `((...))` 结构。如果表达式的结果为零,则 `((...))` 返回状态 1,这对于条件表达式非常有用:`if (( 100-100 )); 然后回显 true;否则回显错误;fi` 如果您的计数器从零开始,您可以使用 `((++x))`,它返回表达式求值之后的值,而 `((x++))` 返回 x 递增之前的值,结果在“假/错误”返回值中。 (2认同)