I have a command like this which tells me if a system has a disk that's getting full (>=70% full):

if [[ -n df -PTh|column -t|awk '{print $6 $7}'|awk -F"%" '{printf "%-9s %-5s\n",$1,$2}'|grep -v -e "Use" -e "Mounted"|awk '$1>70{printf "%-9s %-5s\n",$1,$2}' ]]; then echo "not present"; else echo ">70 is present"; fi

I'd like to want to perform the above command on a remote server using using SSH. Something like:

ssh remoteserver "if [[ -n df -PTh|column -t|awk '{print $6 $7}'|awk -F"%" '{printf "%-9s %-5s\n",$1,$2}'|grep -v -e "Use" -e "Mounted"|awk '$1>70{printf "%-9s %-5s\n",$1,$2}' ]]; then echo "not present"; else echo ">70 is present"; fi"

But when I tried that, I got the following error:

HOST: remoteserver bash: -c: line 0: syntax error in conditional expression bash: -c: line 0: syntax error near /home'
bash: -c: line 0:if [[ -n 92 /home'

First of all, I'd reduce that war-crime of an awk/grep/awk/grep/awk/grep/etc statement down to:

df | awk '$5~/([7-9]|10)[0-9]/'

It does everything you need to check output. You could add a {print ...} clause to the awk statement if you really needed pretty output but given that you're just checking to see if there is output, let's be reasonable about this ☺.

We can also shorten the if/else. I've also corrected the direction — yours was reversed. The whole command now looks like:

[[ -n $(df | awk '$5~/([7-9]|10)[0-9]/') ]] && echo 'ACHTUNG!' || echo 'No >=70s'

I think you'd agree that's just slightly more edifying.

Now you could probably escape this manually now and wrap it in a bash -c "..." but where's the fun in that? Instead let's save the command into a local file (say commands.txt) you can send that to the server like so:

ssh user@host $(<commands.txt)

And you could leave it there. You could actually throw a working version of your original command in there and send that over. This thing is pretty robust... But we don't want to do that... Not when we can be more clever (and cheaty) with the command and do away with the if brackets (which are the things causing the escaping problem).

We are currently running df remotely and processing it remotely. We don't need to do that. We could let ssh just run df and then handle the output locally. That instantly nixes the escaping problem but we're currently tied up in logic [[ brackets ]] which would make the rewrite a bit ugly:

[[ -n $(ssh user@host df | awk '$5~/([7-9]|10)[0-9]/') ]] && .......

We are better than that.
Unlike the monkeys and apes who trod before us, we have exit codes.

When something finishes running, it can return a code. These codes mean various things but generally speaking 0 means "I did exactly what I was designed to do" and anything higher means "I did bad, Bawss".

Conditional hooks like && and || look to the exit code feeding into them to decide whether or not to run. In our case this means if we tell awk to exit in a non-zero way, we can control the code flow:

ssh user@host df | awk '$5~/([7-9]|10)[0-9]/ {exit 1}' && echo 'No >=70s' || echo 'ACHTUNG!'

This also means that it will exit as soon as it detects the first >=70 value. If you had millions of drives, that could save you seconds over other leading-brand scripts. So yes, just to reaffirm what's going on in this final version:

  1. We are connecting to the server and running df remotely
  2. The output is piped back to a local instance of awk
  3. awk looks to see if the fifth column matches a regex to match 70-100
  4. If it does, it exits code 1 and the || code executes (there is a drive over 70% used)
  5. If it doesn't, it exits code 0 and the && code executes.

In previous revisions I also had a NR>1 check in the awk statements but given we're explicitly checking the value of the fifth column (and it has the be a number to work), we can safely omit that too.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.