在while循环中修改shell变量不能按预期工作

时间:2009-04-30 15:59:54

标签: bash shell

这是我写的一个非常简单的bash脚本:

#!/bin/bash

ITEM_LIST=items.txt
LOG_FILE=log.log

TOTAL_ITEMS=$(wc -l ${ITEM_LIST} | awk '{ print $1 }')
let NOT_FOUND=0

cat ${ITEM_LIST} | while read item; do

    grep "${item}" ${LOG_FILE} > /dev/null
    FOUND=${?}
    if [ ${FOUND} -ne 0 ]; then
        let NOT_FOUND=NOT_FOUND+1
        echo "Item not found [${item}] Item not found number: ${NOT_FOUND}"
    fi

done

echo "Total items: ${TOTAL_ITEMS}"
echo "Total not found items: ${NOT_FOUND}"

我想检查日志文件中是否存在某些项目,计算不存在的项目数量,并打印某种报告(最后两次回显)。此时,我正在从cygwin bash shell运行它。

考虑这两个示例文件:

items.txt

first item
second item
third item
fourth item
fifth item

log.log

blahblah blah blah first item blah blah blah
second blah blah item
blah third item blah

脚本的输出:

[17:46:38]:/cygdrive/c/Temp/qpa# ./script2.sh 
Item not found [second item] Item not found number: 1
Item not found [fourth item] Item not found number: 2
Total items: 4
Total not found items: 0

问题:

为什么脚本会打印“Total not found items:0”?它打印循环上每个回显的当前总数(均为NOT_FOUND变量)。

这个shell脚本中有一些不好的做法吗?在哪里以及为什么?

2 个答案:

答案 0 :(得分:6)

你在这里使用的管道:

cat ${ITEM_LIST} | ...

之后将在子shell中执行while循环。但这意味着NOT_FOUND变量不会在父shell中更新,而只会在您执行循环的子shell中更新。

在子shell实例的末尾包含echo命令:

cat ${ITEM_LIST} | { 
  while read item; do
    grep "${item}" ${LOG_FILE} > /dev/null
    FOUND=${?}
    if [ ${FOUND} -ne 0 ]; then
        let NOT_FOUND=NOT_FOUND+1
        echo "Item not found [${item}] Item not found number: ${NOT_FOUND}"
    fi
  done

  echo "Total items: ${TOTAL_ITEMS}"
  echo "Total not found items: ${NOT_FOUND}"
}

Bash FAQ item中也解释了这个问题。希望这可以帮助。

正如常见问题解答所解释的那样,在特定情况下,您也可以将其重写为:

while read item; do
    grep "${item}" ${LOG_FILE} > /dev/null
    FOUND=${?}
    if [ ${FOUND} -ne 0 ]; then
        let NOT_FOUND=NOT_FOUND+1
        echo "Item not found [${item}] Item not found number: ${NOT_FOUND}"
    fi
done < ${ITEM_LIST}

在这种情况下首选第二个选项,因为它将摆脱一个“无用的猫”:)

答案 1 :(得分:0)

使用expr进行数学计算

NOT_FOUND=0; NOT_FOUND=`expr ${NOT_FOUND} + 1`; echo ${NOT_FOUND}

1

@litb是对的,您需要在主脚本中更新NOT_FOUND,而不是在管道命令生成的子shell中更新。