简体   繁体   中英

How to search for “ERROR” string from the output of the test in shell scripting?

I am modifying a shell script, which uses Makefile to test code in different test benches, currently the shell script only echo SUCCESS and FAILURE, i want to modify this shell script to search through the output trace strings which are being written into another file at the same location where the Makefile is called upon namely trace.log. If it finds the word "ERROR"(which is a trace string) i want the test to say FAILURE

!/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
}

I have no background in shell scripting. I understood what's written here by googling the constructs, but still I didn't understand how for t in $tests works and what sed does.

From what i thought would work for my problem is

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

After studying a bit about $? and shell scripting I know this wont work, what should I have to do to achieve this thing?

The for loop for variable in one two three will run the body of the loop with $variable set to one on the first iteration, two on the next, and then to three . When the loop finishes, the variable retains the last value it had in the loop.

sed is a scripting language in its own right, though typically it is only used for trivial string substitutions. The sed script s/regex/replacement/g will replace every match on the regular expression with the static string replacement in the file(s) it processes. (In the absence of the /g flag, only the first occurrence on each input line will be replaced.)

Generally, scripts should examine the exit status from a command, not grep for human-readable strings. The make command already illustrates this, though it is not entirely idiomatic.

Incidentally, the shebang on the first line must start with exactly the two characters #!

Here is a refactoring with comments.

#!/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
}

Notice the addition of the grep after the make . The && says "and" and the ! inverts the exit code from grep (so this is true if grep fails to find any matches). The -q suppresses the output of matches by grep and causes it to stop searching as soon as it finds the first match.

More tangentially, I would regard pushd and popd as intended for interactive use. Shell scripts usually just cd in a subshell and then when the subshell finishes, you are back in the directory where you started. But here, a more fundamental refactoring might be called for.

For a somewhat more complex but hopefully robust refactoring, maybe do

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 \)
}

... though the -quit predicate is very much a GNU find extension (stol... flattered from here ).

This has some assumptions about your test file names which might not be correct. Is test.xml the actual file name of the test, or just a suffix (or even somewhere in the middle)?

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

Should work with any shell.

(Thanks to tripleee useful comments)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM