0

I'm writing this script, which should detect an error after the smart test is done. But I can't get it to detect any error, or not of course.

 if [[ smartctl --log=selftest /dev/sda | awk 'NR>7 {print $4,$5,$6,$7}' | sed 's/offline//g; s/00%//g' != *"Completed without error"* ]; then
 echo "No error detected"
 else echo "Error detected"
 fi

Output:

./test.sh: line 19: conditional binary operator expected
./test.sh: line 19: syntax error near `--log=selftest'
./test.sh: line 19: `if [[ smartctl --log=selftest /dev/sda | awk 'NR>7 {print $4,$5,$6,$7}' | sed  's/offline//g; s/00%//g' != *"Completed without error"* ]]; then'

So obviously I'm doing something wrong. But all the tutorials say two [[]] thingies, but I think the command is quite complex, it doesn't work... How can I make it work?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Florius
  • 367
  • 1
  • 5
  • 17
  • 1
    `[[ ]]` is a specific command, not part of general-purpose `if` syntax. If you don't want to run that command, don't put it in your syntax. – Charles Duffy Jan 31 '17 at 18:28
  • Rather, you need to capture the string output by the pipeline in order to compare it to the given pattern. – chepner Jan 31 '17 at 18:28
  • Removing the `[[ ]]` I get `sed: can't read !=: No such file or directory sed: can't read *Completed without error*: No such file or directory` – Florius Jan 31 '17 at 18:30
  • Yes, the `!=` calls for `[[ ]]`. Suffice to say that this was a long and hairy enough command that it looked like a different common error at first glance. – Charles Duffy Jan 31 '17 at 18:31
  • @CharlesDuffy The typo of `]` for `]]` at the end didn't help that perception. – chepner Jan 31 '17 at 18:32
  • BTW, does smartctl not set its exit status correctly? If it does, you don't need to munge its output at all to determine success; `if ! smartctl --log=selftest /dev/sda; then echo "running smartctl failed"; fi` will suffice. – Charles Duffy Jan 31 '17 at 18:33
  • @chepner, copy paste error, it's correct in my script! CharlesDuffy Correct, it does, however, it should be fully automated, and should detect if the smart test does NOT give "Completed without error" and it needs to be checked out. As I have no idea what else the output could be, I thought this would've been the easiest way to filter. – Florius Jan 31 '17 at 18:40
  • @Florius, ...one of the sticky things about output meant for humans is that it can change without notice between versions and systems (and environment variables -- if the user running your script has a different locale set, then you get a translation that writes that message in a different way). – Charles Duffy Jan 31 '17 at 18:45
  • ...so, if the smartctl docs guarantee an exit of status in the completed-without-error case, then testing that exit status is going to be the most robust and reliable thing to do. – Charles Duffy Jan 31 '17 at 18:47

2 Answers2

2

If you want to do a substring comparison, you need to pass a string on the left-hand side of the = or != operator to [[ ]].

A command substitution, $(), will replace the command it contains with its output, giving you a single string which can be compared in this way.

That is:

smartctl_output=$(smartctl --log=selftest /dev/sda | awk 'NR>7 {print $4,$5,$6,$7}' | sed 's/offline//g; s/00%//g')
if [[ "$smartctl_output" != *"Completed without error"* ]; then
   : ...put your error handling here...
fi

or, a bit less readably:

if [[ "$(smartctl --log=selftest /dev/sda | awk 'NR>7 {print $4,$5,$6,$7}' | sed 's/offline//g; s/00%//g')" != *"Completed without error"* ]; then
   : ...put your error handling here...
fi
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Okay, that works, kind off.. I'm not sure with what the if error compares though... It says error detected although there are none. Is there something wrong with the `!= *"Completed without error"*` comparison? [root@operations ~]# ./test.sh Error detected [root@operations ~]# smartctl --log=selftest /dev/sda | awk 'NR>7 {print $4,$5,$6,$7}' | sed 's/offline//g; s/00%//g' Completed without error Completed without error – Florius Jan 31 '17 at 18:47
  • `declare -p smartctl_output` to print the variable's contents unambiguously. – Charles Duffy Jan 31 '17 at 18:49
  • Okay, got it. I actually didn't think of making it a variable, I was thinking too complex! Thank you for giving me another look on it! – Florius Jan 31 '17 at 18:52
2

You are confusing things. If the command you want to test is smartctl, don't replace it with [[. You want either or, not both. (See also e.g. Bash if statement syntax error)

Anyway, piping awk through sed and then using the shell to compare the result to another string seems like an extremely roundabout way of doing things. The way to communicate with if is to return a non-zero exit code for error.

if smartctl --log=selftest /dev/sda |
   awk 'NR>7 { if ($4 OFS $5 OFS $6 OFS $7 ~ /Completed without error/) e=1; exit }
       END { exit 1-e }'
then
     echo "No error detected"
else
     echo "Error detected"
fi
Community
  • 1
  • 1
tripleee
  • 175,061
  • 34
  • 275
  • 318
  • Your command will always exit 1, though you have `exit 0` before `END` block, for example run this `echo 1 | awk '{exit 0}END{exit 1}'; echo $?` – Akshay Hegde Jan 31 '17 at 18:37
  • 1
    You can do something like this to correct, `awk 'BEGIN{e=1}NR>7 { if ($4 OFS $5 OFS $6 OFS $7 ~ /Completed without error/) e=0} END { exit e&&1 }'` – Akshay Hegde Jan 31 '17 at 18:46
  • That works! Got one question, if the output gives for example error on Disk /dev/sda, will it still says no error detected because it reads the word "error"? Or does it only compare to the complete `/Completed without error/` string? – Florius Jan 31 '17 at 18:51
  • @Florius, `/Completed without error/` will only match that exact string (absent the delimiters) in full. – Charles Duffy Jan 31 '17 at 21:02
  • 1
    @AkshayHegde Thanks for the comments, fixed the exit code handling. – tripleee Feb 01 '17 at 04:00
  • "Completed without error" is a fixed phrase but that *exact* string can be a substring of the output. The thing between the slashes is a regular expression so you can easily extend it if you need more flexibility. – tripleee Feb 01 '17 at 04:02