我有以下简单的脚本,我正在运行循环并希望维护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
答案 0 :(得分:151)
首先,你没有增加柜台。将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
答案 1 :(得分:83)
COUNTER=1
while [ Your != "done" ]
do
echo " $COUNTER "
COUNTER=$[$COUNTER +1]
done
TESTED BASH:Centos,SuSE,RH
答案 2 :(得分:36)
COUNTER=$((COUNTER+1))
在现代编程中是一个非常笨拙的构造。
(( COUNTER++ ))
看起来更“现代”。您也可以使用
let COUNTER++
如果您认为可以提高可读性。有时,Bash提供了太多的做事方式 - 我认为Perl哲学 - 也许Python“只有一种正确的方法”可能更合适。如果有的话,这是一个值得商榷的陈述!无论如何,我建议目标(在这种情况下)不仅仅是增加变量,而是(一般规则)也编写其他人可以理解和支持的代码。符合性对实现这一目标有很大帮助。
HTH
答案 3 :(得分:13)
count=0
base=1
(( count += base ))
答案 4 :(得分:12)
尝试使用
COUNTER=$((COUNTER+1))
而不是
COUNTER=$((COUNTER))
答案 5 :(得分:10)
我认为这个单一的awk调用等同于你的grep|grep|awk|awk
管道:请测试一下。你的上一个awk命令似乎什么都没改变。
COUNTER的问题是while循环在子shell中运行,因此当subshell退出时,对变量的任何更改都会消失。您需要在相同的子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
}
答案 6 :(得分:8)
您可以通过使用进程替换来避免在while
循环周围创建子shell,而不是使用临时文件。
while ...
do
...
done < <(grep ...)
顺便说一句,您应该能够将grep, grep, awk, awk, awk
的所有内容转换为单个awk
。
从Bash 4.2开始,有一个lastpipe
选项
运行a的最后一个命令 当前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
答案 7 :(得分:5)
极简
counter=0
((counter++))
echo $counter
答案 8 :(得分:4)
这就是你需要做的所有事情:
$((COUNTER++))
以下摘录自学习bash Shell ,第3版,第147,148页:
bash 算术表达式与其中的对应词相同 Java和C语言。[9]优先级和关联性是相同的 与C相同。表6-2显示了支持的算术运算符。 虽然其中一些是(或包含)特殊字符,但有 不需要反斜杠 - 逃避它们,因为它们在$((...)之内 语法。
..........................
++和 - 运算符在你想要增量或增量时非常有用 将值减一。[11]它们的工作方式与Java和C相同, 例如, 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/
答案 9 :(得分:2)
有两种情况导致表达式((var++))
对我失败:
如果我将bash设置为严格模式(set -euo pipefail
),并且我将增量从零开始(0)。
从一(1)开始很好,但是零会导致在评估“ ++”时增量返回“ 1”,这是严格模式下的非零返回码失败。
我可以使用((var+=1))
或var=$((var+1))
来逃避这种行为
答案 10 :(得分:1)
这是一个简单的示例
COUNTER=1
for i in {1..5}
do
echo $COUNTER;
//echo "Welcome $i times"
((COUNTER++));
done
答案 11 :(得分:0)
您似乎没有更新counter
脚本,请使用counter++
答案 12 :(得分:0)
源脚本的subshell有问题。 第一个示例,您可能不需要subshell。但是我们不知道“更多动作”下隐藏了什么。 最受欢迎的答案是隐藏的错误,它将增加I / O,并且不能与subshell一起使用,因为它可以恢复内部循环。
不要强行添加'\'号,它将通知bash解释器有关行继续的信息。希望对您或任何人有帮助。但是在我看来,该脚本应该完全转换为AWK脚本,或者使用regexp或perl重写为python,但是多年以来perl的普及度下降了。最好使用python。
没有子外壳的正确版本:
#!/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://example.com"$5"&ip="$2"&date="$7"&time="$8"&end=1"}' |\
awk -F '&end=1' '{print $1"&end=1"}' |\
#( #unneeded bracket
while read WFY_URL
do
echo $WFY_URL #Some more action
COUNTER=$((COUNTER+1))
done
# ) unneeded bracket
echo $COUNTER # output = 0
如果真的需要带子外壳的版本
#!/bin/bash
TEMPFILE=/tmp/$$.tmp #I've got it from the most popular answer
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://example.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 > $TEMPFILE #store counter only once, do it after loop, you will save I/O
)
COUNTER=$(cat $TEMPFILE) #restore counter
unlink $TEMPFILE
echo $COUNTER # output = 0