在 Bash 循环内递增变量

问题描述 投票:0回答:8

我正在尝试编写一个小脚本来计算日志文件中的条目,并且我正在递增一个变量(

USCOUNTER
),我在循环完成后尝试使用该变量。

但此时

USCOUNTER
看起来是 0 而不是实际值。知道我做错了什么吗?谢谢!

FILE=$1

tail -n10 mylog > $FILE

USCOUNTER=0

cat $FILE | while read line; do
  country=$(echo "$line" | cut -d' ' -f1)
  if [ "US" = "$country" ]; then
        USCOUNTER=`expr $USCOUNTER + 1`
        echo "US counter $USCOUNTER"
  fi
done
echo "final $USCOUNTER"

它输出:

US counter 1
US counter 2
US counter 3
..
final 0
bash shell loops
8个回答
68
投票

您在子 shell 中使用

USCOUNTER
,这就是变量未显示在主 shell 中的原因。

代替

cat FILE | while ...
,只做
while ... done < $FILE
。这样,您就可以避免常见问题:我在管道中的循环中设置变量。为什么循环结束后它们就消失了?或者,为什么我无法通过管道读取数据?:

while read country _; do
  if [ "US" = "$country" ]; then
        USCOUNTER=$(expr $USCOUNTER + 1)
        echo "US counter $USCOUNTER"
  fi
done < "$FILE"

注意我还用 $() 替换了 `` 表达式。

我还将

while read line; do country=$(echo "$line" | cut -d' ' -f1)
替换为
while read country _
。这允许您说
while read var1 var2 ... varN
,其中
var1
包含该行中的第一个单词,
$var2
等等,直到
$varN
包含剩余内容。


32
投票

极简主义

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

此方法使用此处定义的算术扩展:Bash Arithmetic Expansion

有效表达式在这里定义:Shell Arithmetic


30
投票
while read -r country _; do
  if [[ $country = 'US' ]]; then
    ((USCOUNTER++))
    echo "US counter $USCOUNTER"
  fi
done < "$FILE"

16
投票

您得到

final 0
是因为您的
while loop
正在子(shell)进程中执行,并且在那里所做的任何更改都不会反映在当前(父)shell 中。

正确的脚本:

while read -r country _; do
  if [ "US" = "$country" ]; then
        ((USCOUNTER++))
        echo "US counter $USCOUNTER"
  fi
done < "$FILE"

10
投票

我在 while 循环中遇到相同的 $count 变量丢失问题。

@fedorqui 的答案(以及其他一些)是对实际问题的准确答案:子 shell 确实是问题所在。

但这让我遇到了另一个问题:我不是通过管道传输文件内容...而是一系列管道和 grep 的输出...

我的错误示例代码:

count=0
cat /etc/hosts | head | while read line; do
  ((count++))
  echo $count $line
done
echo $count

以及我的修复感谢该线程和进程替换的帮助

count=0
while IFS= read -r line; do
  ((count++))
  echo "$count $line"
done < <(cat /etc/hosts | head)
echo "$count"

1
投票
USCOUNTER=$(grep -c "^US " "$FILE")

0
投票

增加变量可以这样完成:

  _my_counter=$[$_my_counter + 1]

可以使用 grep 来计算列中某个模式出现的次数

 grep -cE "^([^ ]* ){2}US"

-c

([^ ]* )
检测结肠

{2}
结肠号

US
你的图案


0
投票

使用以下 1 行命令使用短语特异性更改 Linux 中的许多文件名:

find -type f -name '*.jpg' | rename 's/holiday/honeymoon/'

对于所有扩展名为“.jpg”的文件,如果它们包含字符串“holiday”,请将其替换为“honeymoon”。例如,此命令会将文件“ourholiday001.jpg”重命名为“ourhoneymoon001.jpg”。

此示例还说明了如何使用 find 命令发送扩展名为 .jpg (-name '*.jpg') 的文件列表 (-type f),以通过管道 (|) 进行重命名。重命名然后从标准输入读取其文件列表。

© www.soinside.com 2019 - 2024. All rights reserved.