如何在Shell脚本中从测试的输出中搜索“ ERROR”字符串?

时间:2019-01-30 17:47:12

标签: bash shell scripting grep

我正在修改一个shell脚本,该脚本使用Makefile在不同的测试台上测试代码,当前该shell脚本仅回显SUCCESS和FAILURE,我想修改此shell脚本以搜索正在写入的输出跟踪字符串在调用Makefile的同一位置的另一个文件,即trace.log。如果它找到单词“ ERROR”(这是一个跟踪字符串),我希望测试说出失败

!/usr/bin/env bash
#set -e

BASEDIR=$(pwd)
REPOROOT=`git rev-parse --show-toplevel`


RED="`tput setaf 1`"
GREEN="`tput setaf 2`"
BLUE="`tput setaf 4`"
NORM="`tput sgr0`"

Test() {
    pushd ${REPOROOT}/rr/Fmda/body > /dev/null
    tests=`find . -type f | grep "test.xml" | sed "s/test\.xml//"`
    for t in $tests; do
      testname=`echo $t | sed "s/\.\///" | sed "s/\/test\///"`
      echo -e "${BLUE}EXECUTING $testname TEST${NORM}"
      pushd $t > /dev/null
      make test_svp
      if [ $? -eq 0 ]; then
        echo -e "${GREEN}SUCCESS IN $testname TEST MOVING ON!${NORM}"
      else
        echo "${RED}FAILURE IN $testname TEST${NORM}"
        exit 1
      fi
      popd > /dev/null
     done
     popd > /dev/null
}

我没有Shell脚本方面的背景知识。我通过搜索结构了解了这里写的内容,但仍然不了解for t in $tests的工作原理和sed的工作。

我认为对我的问题有用的是

error=trace.log|grep "ERROR"
if [ $? -eq 0] && [ !error ]; then 

研究了$?和shell脚本后,我知道这行不通,我应该怎么做才能实现这一目标?

2 个答案:

答案 0 :(得分:2)

for循环for variable in one two three将在第一次迭代时将$variable设置为one,在下一次迭代two时运行循环主体。然后到three。循环结束后,变量将保留循环中的最后一个值。

sed本身就是一种脚本语言,尽管通常它仅用于琐碎的字符串替换。 sed脚本s/regex/replacement/g将用其处理的文件中的静态字符串replacement替换正则表达式上的所有匹配项。 (在没有/g标志的情况下,将仅替换每个输入行上的第一个匹配项。)

通常,脚本应检查命令的退出状态,而不是人类可读字符串的grep。 make命令已经说明了这一点,尽管它并不完全是习惯用法。

顺便说一句,第一行的shebang必须以两个字符#!开头

这里是带有注释的重构。

#!/usr/bin/env bash
#set -e

# Use lower case for your private variables
# BASEDIR was never used for anything
# Consistently use modern command substitution syntax
reporoot=$(git rev-parse --show-toplevel)

RED="$(tput setaf 1)"
GREEN="$(tput setaf 2)"
BLUE="$(tput setaf 4)"
NORM="$(tput sgr0)"

Test() {
    # Maybe don't pushd; see below
    # Quote the variable properly 
    pushd "$reporoot/rr/Fmda/body" > /dev/null
    # Refactor to avoid useless use of grep
    tests=$(find . -type f | sed -n "s/test\.xml//p")
    for t in $tests; do
      # Proper quoting; refactor to a single sed script
      # Fix sed syntax error (too many slashes)
      testname=$(echo "$t" | sed "s/\.\//;s%/test/%%")
      echo -e "${BLUE}EXECUTING $testname TEST${NORM}"
      pushd "$t" > /dev/null
      # Use "if" like it was meant to
      # Add grep condition
      if make test_svp && ! grep -q "ERROR" trace.log; then
        echo -e "${GREEN}SUCCESS IN $testname TEST MOVING ON!${NORM}"
      else
        echo "${RED}FAILURE IN $testname TEST${NORM}"
        exit 1
      fi
      popd >/dev/null
    done
    popd >/dev/null
}

请注意,在grep之后添加了make&&说“和”,而!反转来自grep的退出代码(因此,如果grep找不到任何匹配项,则为true)。 -q通过grep抑制匹配的输出,并使其在找到第一个匹配项后立即停止搜索。

更切地讲,我认为pushdpopd是供交互使用的。 Shell脚本通常只是在子Shell中cd,然后在子Shell完成后,您便回到了开始的目录中。但是在这里,可能需要更基本的重构。

对于稍微复杂但希望可靠的重构,也许可以

Test() {
    find "$reporoot/rr/Fmda/body" -name 'test.xml' \
        \( -execdir sh -c '
            # XXX TODO: maybe replace with parameter substitutions
            testname=$(echo "$1" | sed "s%/test\.xml$%%;s/\.\//;s%/test/%%")
            echo -e "${BLUE}EXECUTING $testname TEST${NORM}"
            make test_svp &&
            ! grep -q "ERROR" trace.log &&
            echo -e "${GREEN}SUCCESS IN $testname TEST MOVING ON!${NORM}" ||
            { 
              echo "${RED}FAILURE IN $testname TEST${NORM}"
              exit 1; }' _ {} \; -o -quit \)
}

...尽管-quit谓词在很大程度上是GNU find的扩展名(从here受宠若惊)。

关于测试文件名的一些假设可能不正确。 test.xml是测试的实际文件名,还是只是后缀(甚至是中间的某个地方)?

答案 1 :(得分:0)

log=file.log
if grep ERRORS "$log" ; then echo BIG FAILURE ; fi

应该与任何外壳一起使用。

(感谢三人有用的评论)