views:

305

answers:

3

I wrote a tiny Bash script to find all the Mercurial changesets (starting from the tip) that contains the string passed in argument:

#!/bin/bash

CNT=$(hg tip | awk '{ print $2 }' | head -c 3)
while [ $CNT -gt 0 ]
do
    echo rev $CNT
    hg log -v -r$CNT | grep $1       
    let CNT=CNT-1
done

If I interrupt it by hitting ctrl-c, more often than not the command currently executed is "hg log" and it's that command that gets interrupted, but then my script continues.

I was then thinking of checking the return status of "hg log", but because I'm piping it into grep I'm not too sure as to how to go about it...

How should I go about exiting this script when it is interrupted? (btw I don't know if that script is good at all for what I want to do but it does the job and anyway I'm interested in the "interrupted" issue)

+1  A: 

The $PIPESTATUS variable will allow you to check the results of every member of the pipe.

Ignacio Vazquez-Abrams
+2  A: 

Place at the beginning of your script: trap 'echo interrupted; exit' INT

Edit: As noted in comments below, probably doesn't work for the OP's program due to the pipe. The $PIPESTATUS solution works, but it might be simpler to set the script to exit if any program in the pipe exits with an error status: set -e -o pipefail

Arkku
The trap command only works with processes that don't trap interrupts themselves. If the script was receiving the interrupt, it would be aborting itself already.
Dan Story
Ah, sorry about that, I missed the pipe in the OP's script. A better answer might be to:set -e pipefail
Arkku
Your first solution (the trap) would work with any process that doesn't trap interrupts itself -- *find* is a good example of a long-running process that will pass through the interrupt, causing the script to abort. The trap can be used to prevent the script from aborting immediately in order to do cleanup, kind of like a destructor or finalizer. It's a handy command to know about.
Dan Story
That should be `set -o pipefail`.
Dennis Williamson
Actually, I meant set -e -o pipefail, the -e to make the program script exit on error and -o pipefail to make the pipe fail if hg returns an error status.
Arkku
+3  A: 

Rewrite your script like this, using the $PIPESTATUS array to check for a failure:

#!/bin/bash

CNT=$(hg tip | awk '{ print $2 }' | head -c 3)
while [ $CNT -gt 0 ]
do
    echo rev $CNT
    hg log -v -r$CNT | grep $1
    if [ 0 -ne ${PIPESTATUS[0]} ] ; then
            echo hg failed
            exit
    fi     
    let CNT=CNT-1
done
Dan Story
Thanks a lot, +1 to all. Working fine :)
Webinator
I should probably point out that both $PIPESTATUS and the pipefail option that Arkku mentioned are Bash version 3+ specific. They're not supported by other shells.
Dan Story
@Dan Story: thanks, on my Debian Linuxes I do indeed have Bash 3+ but on my Macs it's Bash 2.05.
Webinator
Really? That's surprising. I'm running bash 3.2.48 on my MacBook Pro, and I don't recall upgrading it myself. What version of OSX are you running?
Dan Story
@Dan Story: Leopard on my MacBook Pro that just had a hard disk crash this morning, so can't check ;) And 10.4.11 on my Mac Minis (that's the ones that have Bash 2.05), which I need specifically to test because we're shipping apps on 10.4, 10.5 and 10.6 and so we do need these older versions of OS X to test our software.
Webinator