-   Programming (
-   -   Bash Shell program question - if error then mailx (

khandu 04-01-2012 11:36 PM

Bash Shell program question - if error then mailx
Hey Guys

Multiple questions

Basically I am trying to write a shell script in bash which does the following

1) Does some export and zip -- this part is easy and done
2) create a folder if it does not exist
3) Move files to that folder in (2)
4) if an error happens in step (1-3) then send a mail (mailx)


What I have figured is


start if loop
start for loop for number of files
    export files
mkdir -p /location/to/store/file
mv *.files /location/to/store/file
echo "Didn't work" | mailx -s Error on `hostname`

Now this will email everytime even if there is or isin't an error..

How do i do some kind of error trapping here that if

1) export file OR mkdir OR mv fails (or any return other than 0 from what i understand)

then the echo mailx gets kicked off


druuna 04-02-2012 03:19 AM


$? tells you if a command succeeded or failed. If the output is 0 (zero), the command ran the way it should have, anything else points to a failure. Using $? only works when used immediately after the command you need to check. Have a look at this:

ls foobar
if [[ "$?" != "0" ]] # is $? anything but 0
  echo "oeps..."    # your code goes here...
  exit 1            # stop the script

The exit 1 makes sure your script stops running once it encounters an error.

If you need to check the success of a command multiple times you can either use the above code multiple times or make a function:

function errorHandler ()
  echo "oeps..."    # your code goes here...
  exit 1            # stop the script

ls foobar
[[ "$?" != "0" ]] &&  errorHandler  # is $? anything but 0

mv FooBar /x/y/z
[[ "$?" != "0" ]] &&  errorHandler  # is $? anything but 0

Hope this helps.

David the H. 04-02-2012 05:53 AM

You can also use any command directly in an if construct, not just the "["-type tests. The exit code is what if is checking for, after all.


if maincommand ; do
        <subcommands upon success (exit code =0)>
        <subcommands upon failure (exit code >0)>

Nominal Animal 04-02-2012 06:11 AM

I fully agree with Druuna.

Since the exit status value ($?) itself does not matter -- we are only interested if it was nonzero --, I prefer to use a bit different form of a shell function (Fatal) for this:

FatalSubject='This thing just failed, see'

function Fatal () {
    if [ $? -ne 0 ]; then
        echo -e "$@" | mailx -s "$FatalSubject" "$FatalRecipient"
        exit 1
    return 0

The idea is that you append || Fatal 'Description' after each command that should not fail. If, and only if, the left side of the || fails, the Description is e-mailed to $FatalRecipient using subject $FatalSubject :

cp -pf oldfile newfile || Fatal "Cannot copy 'oldfile' to 'newfile'."
If you want to fail unconditionally, use

false || Fatal 'Description'
since false will do nothing but fail; it does nothing but return a nonzero exit status.

Here is a real world example:

A typical operation in a script like this is to create a temporary working directory. This is how to do it so that it will be automatically deleted when the script exits, no matter why the script exits. If the temporary directory cannot be created, the script is aborted, and an error e-mail sent:

tempdir="$(mktemp -d)" || Fatal 'Cannot create a temporary directory.'

trap "rm -rf '$tempdir'" EXIT

The trap command removes the directory whenever the interpreter exits. (Because the command is in double quotes, the value of $tempdir is evaluated right then and there, when the trap is set. This means that even if you change tempdir later on, it will not affect the trap; regardless of the value of $tempdir when the script exits, the trap will always remove the original directory.)

Let us expand this example a bit further.

Let us say you wish to apply a sed operation to say all files in and under /var/lib/mystuff/. The following form requires Bash (read -rd ""), but does support all possible file names, even those containing whitespace or newlines or other weird characters:


find /var/lib/mystuff/ -type f -print0 | while read -rd "" FILE ; do

    cp -f --preserve=all "$FILE" "$tempdir/copy" || Fatal "Could not backup '$FILE'."

    sed -e 's|something|other|g' "$FILE" > "$tempdir/copy" || Fatal "Could not edit '$FILE' using sed."

    mv -f "$tempdir/copy" "$FILE" || Fatal "Could not replace '$FILE'."


The LANG and LC_ALL are used to set the POSIX aka C locale, just in case you happen to have filenames with non-UTF-8 byte sequences in them. If you use an UTF-8 locale (and most of us do), the tools will abort if they encounter a non-UTF-8 byte sequence in a file name or other string. Setting the locale to POSIX tells tools to treat all names and strings as eight-bit byte sequences.

Note that this affects the sed , too. In the POSIX locale, does not match [a-z]. In the en_US.UTF-8 locale (and all other UTF-8 locales), it does! If you need or want that behaviour, you can always just add LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_CTYPE=en_US.UTF-8 before the sed command, on the same line, to change the locale only for the sed command.

The loop body will first copy the original file into the temporary directory, preserving all metadata (from ownership and mode, up to extended attributes). Ownership is only preserved if the user running the command is allowed to. This copy is done not to save the data, but to save the metadata; this happens to be the easiest way to copy all metadata.

The sed command overwrites the contents of the temporary copy. If it fails, the original file will be intact. Redirecting to the file does not change its metadata (other than size and modification timestamp).

Finally, the original file is replaced with the edited copy. If $tempdir is on the same filesystem, this is guaranteed to leave you with either the original file, or the new file, but never a broken copy. (For different filesystems, it depends on mv implementation; I believe Coreutils mv has the same guarantee across filesystems if using ext2/ext3/ext4/xfs/reiserfs.)

While the above sequence may look a bit cumbersome, it practically guarantees it will always Do The Right Thing. If it fails, the original file is kept intact. If it succeeds, only the contents of the file are changed (although actually the entire file is replaced). For example, SELinux security context, POSIX ACLs, and all extended attributes the file might have, should stay intact.

Moreover, if it fails for any reason, you will always get an e-mail message describing the reason. You could even add

        logger -p 'local.error' -i -t 'MyScript' "$@"
just before the exit 1 in the Fatal function body, to log the error message in the system log, in addition to sending an e-mail. See man logger for details on its use.

Hope this helps,

Dafydd 04-20-2012 06:55 PM

While I was not part of this discussion, let me say that this is exactly the information I was seeking and answered my would be question completely.

LinuxQuestions comes through again.
Thank you guys.

I needed tobe sure the script was killed should an error for any reason pop up.

# check if there is no command line argument
if [ $# -eq 0 ]
echo "You forgot the information: Month Year seperated by a space."

function errorHandler ()
  echo ".....process failed."    # your code goes here...
  exit 1            # stop the script


        cd work
                [[ "$?" != "0" ]] &&  errorHandler;
        cp /media/VOLUMELABE/DCIM/101EKAIO/*.jpg .
                [[ "$?" != "0" ]] &&  errorHandler;
        convert *.jpg -adjoin $MONTH-$YEAR.pdf 
                [[ "$?" != "0" ]] &&  errorHandler;
        cp $MONTH-$YEAR.pdf ../$YEAR
                [[ "$?" != "0" ]] &&  errorHandler;
        rm *.jpg *.pdf
                [[ "$?" != "0" ]] &&  errorHandler;
        rm /media/VOLUMELABE/DCIM/101EKAIO/*.jpg
                [[ "$?" != "0" ]] &&  errorHandler;


All times are GMT -5. The time now is 12:10 PM.