0

I'm having an issue with tail & grep in shell script if statement. If I run tail -5 mylog.log | grep -c "Transferred: 0" in shell, it runs as it should, but in this shell script if statement:

# Parse log for results
if [ tail -1 "$LOGFILE" | grep -c "Failed" ] ; then
        RESULT=$(tail -1 "$LOGFILE")
elif [ tail -5 "$LOGFILE" | grep -c "Transferred:            0" ] ; then
        RESULT=""
else
        RESULT=$(tail -5 "$LOGFILE")
fi

I get

... [: missing `]'
grep: ]: No such file or directory

for both of the grep lines.

It's clearly to do with the closing ] being seen as part of the grep (and thus missing) but I'm using the correct whitespace so I can't figure out what's going on? What am I doing wrong here?

Thanks, PJ

5
  • [ ] is not part of if syntax. It's a separate command. Commented Jan 7, 2017 at 2:11
  • And frankly, this is just plain inefficient -- it's calling tail at least twice. Much, much better to call it once and then test the result in native bash. Commented Jan 7, 2017 at 2:12
  • 2
    BTW, consider running your code through shellcheck.net before asking questions here. Commented Jan 7, 2017 at 2:15
  • How would I get around this by calling tail only once? Btw, thanks for the shellcheck.net. Commented Jan 7, 2017 at 2:20
  • I've extended my answer to show an approach with only one tail invocation (and no grep invocations at all). Commented Jan 7, 2017 at 2:24

1 Answer 1

5

Immediate Issue / Immediate Fix

Take out the brackets.

if tail -1 "$logfile" | grep -q "Failed" ; then

[ is not part of if syntax. Rather, it's a synonym for the command named test (which is typically both available as a shell builtin and an external binary, like /bin/test or /usr/bin/test).

Thus, your original code was running [ tail -1 "$logfile", and piping its result to grep -q "Failed" ]. The first [ was failing because it didn't see an ending ] -- which is mandatory when invoked by that name rather than with the name test -- and also because its parameters weren't a test it knew how to parse; and the second grep didn't know what the ] it was being piped meant, trying to find a file by that name.


Other Notes

Try to run external commands -- like tail -- as little as possible. There's a very significant startup cost.

Consider the following, which runs tail only once:

#!/bin/bash
#      ^^^^- IMPORTANT: bash, not /bin/sh

last_5_lines="$(tail -5 "$logfile")"
last_line="${last_5_lines##*$'\n'}"
if [[ $last_line = *Failed* ]]; then
  result=$last_line
elif [[ $last_5_lines =~ 'Transferred:'[[:space:]]+'0' ]]; then
  result=''
else
  result=$last_5_lines
fi
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the detailed explanation!
BTW, to explain the all-lowercase variables -- see pubs.opengroup.org/onlinepubs/009695399/basedefs/…; all-uppercase names are used for variables with meaning to the shell or operating system, whereas variables with at least one lowercase character in their names are reserved for application use. That's only black-letter POSIX for environment variables, but since setting a regular shell variable will overwrite any like-named environment variable, the convention necessarily applies to them as well if you want to avoid conflicts.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.