druid
druid

Reputation: 165

If condition that matches if grep outputs at least one result even though the exit code is 2 due to some permission denied

I am trying to build a script that will search for a pattern in log files present in a folder. This folder contains a mix of uncompressed log files, and of logs compressed in gunzip files. I only want to search in the gunzip files if the pattern wasn't found in the uncompressed log files. Constraint: the user that triggers the script will never have permission to read some of the files in this folder.

The problem is that even though grep actually outputs results or not, it also encounters some permission denied. And because of this the exit code is always "2". E.g when it outputs results:

[user@mypc ~]$ grep -rnw '/usr/toto/log' -e "pattern"
grep: /usr/toto/log/privileged.log.2025-01-10: Permission denied
grep: /usr/toto/log/privileged.out.1: Permission denied
/usr/toto/log/testontargz.log:1:pattern
grep: /usr/toto/log/privileged.out.3: Permission denied
grep: /usr/toto/log/privileged.log.2025-01-17: Permission denied
grep: /usr/toto/log/privileged.out.2: Permission denied
/usr/toto/log/teston2.log:1:pattern

[user@mypc ~]$ echo $?
2

I know that grep -rnws will get rid of the "Permission denied" in the output of the terminal, but it obviously won't change the exit code from "2" to "1" when it doesn't output results (or "0" when it actually output results).

I've been trying to use "$?" in my if statement to match the exit code 1, but it will obviously never succeed in the current state. So I suppose that using "$?" is not the right choice here but I'm not that good a scripting... How can I modify this script?

#!/bin/sh
printf '\n'

# Search for the error in not compressed log files
printf '%s\n' ">> Errors found in the log file(s):"
grep -rnws '/usr/toto/log' -e "$1"

# Only search in the compressed files if the above grep didn't output any actual result
if [ $? -eq 1 ]; then
    # Search for the error in compressed log files
    printf '\n'
    pattern=$1; shift

    for x do
      if case "$x" in
          *.gz|*.[zZ]) <"$x" gzip -dc | grep -q -e "$pattern";;
         esac
      then
        printf '%s\n' ">> Error found in one of the log files that is stored in the file $x"
      fi
    done
fi

printf '\n'

I'm calling the script like this: myscript.sh patterntofind /usr/toto/log/compressedfile.*.gz

Thank in advance

Upvotes: 2

Views: 109

Answers (3)

KamilCuk
KamilCuk

Reputation: 141698

Does it have to be optimized that much? I mean, it's just as simple as:

pattern=$1
shift
for i in "$@"; do
   if [[ -f "$i" && -r "$i" ]]; then
      case "$x" in
      *.gz|*.[zZ]) compressed+=("$i") ;;
      *) uncompressed+=("$i") ;;
      esac
   fi
done
if ! grep "$pattern" "${uncompressed[@]}"; then
   zgrep "$pattern" "${compressed[@]}"
fi

Upvotes: 0

Ed Morton
Ed Morton

Reputation: 204416

The GNU guys really messed up when they gave grep options to find files (e.g. -r). There's a perfectly good existing tool to find files with an extremely obvious name - find.

To find files you have permission to read and then within those files Globally match a Regular Expression and Print the result (i.e. g/re/p, what the tool named grep was created to do) would be:

find /usr/toto/log -type f -readable -exec grep 'pattern' {} +

You can also add -name '*.gz', etc. to find files ending with .gz, etc. to control which extensions find finds before calling grep (or some other command/pipeline on those files, e.g. (again untested)

find /usr/toto/log -type f -readable \( -name '*.gz' -o -name '*.[zZ]' \) -exec zgrep 'pattern' {} +
find /usr/toto/log -type f -readable ! \( -name '*.gz' -o -name '*.[zZ]' \) -exec grep 'pattern' {} +

Upvotes: 2

Gordon Davisson
Gordon Davisson

Reputation: 126028

It's kind of a kluge, but you can use:

grep -rnw '/usr/toto/log' -e "pattern" | grep '.*'

Explanation: The second grep just searches the output of the first for... anything. Any output at all, i.e. any lines the first grep matched. If it finds that, it succeeds; but if there's no output from the first grep, it'll fail.

Caveats: If you have bash's pipefail option set, then the error from the first grep will show up in $? if the second grep succeeds; in this case, check ${PIPESTATUS[1]} instead of $?. Also, since the regular output from the first grep goes through the second, but its error messages go straight to the terminal (via stderr), the various output may arrive out of order.

Upvotes: 4

Related Questions