LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - General (https://www.linuxquestions.org/questions/linux-general-1/)
-   -   Script help (https://www.linuxquestions.org/questions/linux-general-1/script-help-694256/)

Diggy 01-01-2009 10:27 AM

Script help
 
Hello, and Happy New Year, to all.

I've created a bash script to back up certain critical directories using rsync. It works fine, for the most part, but I have encountered a couple of problems:

1) I want the script to test for the presence of the backup destination directories. If they exist already, then send emails to that effect and exit. However, I'm not sure how to test for the separate directories before exiting (hope that's clear). Here's my code snippet:

# Create backup directories. If they already exist then exit.
#
if [ -d $DESTDIR/$DATE ] ; then
echo "$DESTDIR/$DATE already exists, so backup must already be done" \
|mailx -s "Backup Summary `date +"%m/%d/%y %r"`" $EMAIL
exit
elif [ ! -d $DESTDIR/$DATE ] ; then
mkdir $DESTDIR/$DATE
fi

if [ -d $DESTDIR2/$DATE ] ; then
echo "$DESTDIR2/$DATE already exists, so backup must already be done" \
|mailx -s "Backup Summary `date +"%m/%d/%y %r"`" $EMAIL
exit
elif [ ! -d $DESTDIR2/$DATE ] ; then
mkdir $DESTDIR2/$DATE
fi

As you can see, if the first argument is true, the second is never tested because we've already exited. How can I make the script test for both, and send (an) email(s) before exiting?

2) I want directories older than 14 days to be deleted. The script works if I run it manually, but not if run from cron. Here's the code snippet:

if [ -d $DESTDIR ]; then
find $DESTDIR -maxdepth 1 -mtime +$DAYSOLD -exec rm -Rf {} \;
fi

if [ -d $DESTDIR2 ]; then
find $DESTDIR2 -maxdepth 1 -mtime +$DAYSOLD -exec rm -Rf {} \;
fi

I've seen posts to the effect that the full path must be used, rather than a variable, if the script is to work from cron. But, does anyone know of any way I can make this work using variables?

Many thanks for your help!

Diggy

mesiol 01-01-2009 11:05 AM

Hi,

do not run your test in 2 statements, create one statement using logical AND (||) some like this:

if [ -f $DESTINATION1 ] || [ -f DESTINATION2 ];
then echo BACKUP_EXISTS;
exit 1;
else
echo NO_BACKUP_DO_SOMETHING;
fi

This will prevent you from exiting during your first test before second test is done.

There is also a logical OR (&&)

Hope i could help.

Diggy 01-01-2009 11:16 AM

Thanks so much! That makes sense. But, as I think more about it, I realize that my logic is flawed. If one backup destination directory exists, but the other doesn't, then my script should ignore any further commands having to do with existing directory, but continue with commands for the non-existing one. Can I build upon what you've suggested to accomplish that?

Diggy

openSauce 01-02-2009 07:31 AM

Quote:

Originally Posted by Diggy (Post 3393557)
Thanks so much! That makes sense. But, as I think more about it, I realize that my logic is flawed. If one backup destination directory exists, but the other doesn't, then my script should ignore any further commands having to do with existing directory, but continue with commands for the non-existing one. Can I build upon what you've suggested to accomplish that?

Diggy

Sure. Just remove the exit command from your if block. Also, a general point about if/else:

Code:

if [ sometest ]; then
  do something;
elif [ ! sometest ]; then
# there's no need to include the negation of your test in the
# line above.  we know 'sometest' returns false as we're
# already in the 'else' block. Just use 'else' instead of 'elif'
fi

Re your 2nd question, any script run from the shell should be runnable from cron. Are you failing to initialise your variables when you run the command from cron? How are they initialised in the script which you run manually?

Diggy 01-02-2009 08:10 AM

Thanks for your reply, openSauce.

If I remove the exit command from my if block, what happens if the directories are already created (thus indicating that the script/backup was already run)? I wouldn't want the backup to be done again. Hence the test.

As for initializing my variables, at the top of my delete directory script:

DESTDIR=/backup-external #where to back up to - destination
DESTDIR2=/public/nightly_backup #where to back up to - destination
DAYSOLD="13" #delete backup directories more than x+1 days old

Again, if I run the script manually, works a treat. But from cron, no joy.

Your continued help is very much appreciated.

Diggy

openSauce 01-02-2009 08:38 AM

Sorry, forgot to mention, your backup should be in the else block. So the general logic is

Code:

if [ backup already done ]; then
  send an email
else
  do backup
fi

"do backup" could be replaced by a function call if you know how to declare functions, to avoid duplicating code for the different directories. Or you can just copy/paste the backup code you already have.

=================================================

When you run from cron, are you calling exactly the same script which works if you run it manually, or are you just running
Code:

if [ -d $DESTDIR2 ]; then
find $DESTDIR2 -maxdepth 1 -mtime +$DAYSOLD -exec rm -Rf {} \;
fi

in your crontab?

If you are running the same script (as you should be), try redirecting std out and std err to a file and see what error messages you get. It could be a permissions issue, or perhaps the script is not in cron's $PATH?

openSauce 01-02-2009 08:40 AM

PS: mesiol mixed up and and or. And is &&, or is ||.

Diggy 01-02-2009 08:52 AM

I wish I knew how to create a function, and to put the script in cron's path (the latter might be the solution to my problem. I'll have to RTFM :-) ). As a starter, and not to be lazy, might you share how to do these with me?

Many thanks.

Diggy

i92guboj 01-02-2009 08:52 AM

Quote:

Originally Posted by mesiol (Post 3393543)
Hi,

do not run your test in 2 statements, create one statement using logical AND (||) some like this:

openSauce already cleared that. That's OR, not AND. But besides that, I would use AND (the real one). Unless the OP really mean OR. This code that you posted will exit if one of the destinations exist.

Code:

if [ -f $DESTINATION1 ] ||  [ -f DESTINATION2 ];
        then echo BACKUP_EXISTS;
            exit 1;

So, if "$DESTINATION1" exist, "$DESTINATION2" is not even checked (which means it might not exist). So, you probably want to use &&, and not ||.

Also, the colons are not needed. They are redundant. You only need then when you want to concatenate two sentences on a single line. So, I'd either do:

Code:

if [ -f $DESTINATION1 ] ||  [ -f DESTINATION2 ]
  then echo BACKUP_EXISTS
  exit 1

or

Code:

if [ -f $DESTINATION1 ] ||  [ -f DESTINATION2 ]; then
  echo BACKUP_EXISTS
  exit 1

But, in first place, I think that the first idea was maybe better. You want to check separately both things, and not concatenate them. Why? Because you might need to backup one destination, but not the other...

I also suggest declaring functions, for both the backup and the exit+send mail functionalities.

openSauce 01-02-2009 09:05 AM

Quote:

Originally Posted by Diggy (Post 3394530)
I wish I knew how to create a function, and to put the script in cron's path (the latter might be the solution to my problem. I'll have to RTFM :-) ). As a starter, and not to be lazy, might you share how to do these with me?

Many thanks.

Diggy

Declaring functions isn't essential for now, although it'll make your code easier to read, debug and maintain. I've seen this guide recommended several times as an intro to bash scripting (don't be put off by the "advanced" in the title! It starts with the basics).

For now, the relevant line in crontab should be something like this.
Code:

* * * * * /home/[your_user_name]/delete_dir_script.sh >/home/[your_user_name]/delete_dir_log 2>&1
Save the script you want to run in your home directory with the name delete_dir_script.sh. And replace at least some of the *'s with numbers, unless you want the script to run every minute! The part beginning with '>' redirects all the script's output to a file called delete_dir_log in your home directory, and 2>&1 redirects all error messages to the same file. Once the script is run, you can examine this file (and paste it here if you need to) to work out what the problem is.

cheers

OS

Diggy 01-02-2009 09:12 AM

I guess in need i need to do this:

If exists a and b, then email and exit.
If exists a but not b, then backup b, email, and exit.
If does not exist a but exists b, then backup a, email, and exit.
If does not exist a or b, then backup a and b, email, and exit.

Just not sure how to code that.

Diggy

i92guboj 01-02-2009 09:31 AM

Quote:

Originally Posted by Diggy (Post 3394553)
I guess in need i need to do this:

If exists a and b, then email and exit.
If exists a but not b, then backup b, email, and exit.
If does not exist a but exists b, then backup a, email, and exit.
If does not exist a or b, then backup a and b, email, and exit.

Just not sure how to code that.

Diggy

The simplest way is your original idea, without the exits. I've used some cleaning and made a function just for the sake of doing an example.

Code:

function foo() {
  echo "$1 already exist, so backup must already be done." | \
    mailx -s "Backup Summary `date +"%m/%d/%y %r"`" $EMAIL
}

if [ -d "$DESTDIR/$DATE" ]; then
  foo "$DESTDIR/$DATE"
else
  mkdir "$DESTDIR/$DATE"
fi

if [ -d "$DESTDIR2/$DATE" ]; then
  foo "$DESTDIR/$DATE"
else
  mkdir "$DESTDIR2/$DATE"
fi

This way, the ifs do not interfere with each other, which is what you intend.

Diggy 01-02-2009 09:44 AM

i92guboj,

Ah, suberb. But, does the function keep either backup (or both backups) from happening if they're already complete (and by complete, my script simply checks for the existence of the destination directories)?

Diggy


PS - Thanks, openSauce. I'll send script output to a file, as you suggest, and report results (can't run script until tonight, though).

i92guboj 01-02-2009 10:05 AM

Quote:

Originally Posted by Diggy (Post 3394598)
i92guboj,

Ah, suberb. But, does the function keep either backup (or both backups) from happening if they're already complete (and by complete, my script simply checks for the existence of the destination directories)?

Diggy


PS - Thanks, openSauce. I'll send script output to a file, as you suggest, and report results (can't run script until tonight, though).

The checks are the same you did on your first post. Read the code carefully, and you will understand it. If the check is sucessful (dir exist), the foo function is called (and it sends the mail), if not, the mkdir is called. It's exactly the same thing you were doing on the first post. Except that I don't call exit.

The checks are still done out of the function, on the if-then-else blocks.

Diggy 01-02-2009 10:24 AM

Sorry, I should have included the entire script for clarity. Here's the remainder:

# Backup source directories to destination directories
for d in $LIST; do
rsync -az $SRCDIR/$d/ $DESTDIR/$DATE/$d
rsync -az $SRCDIR/$d/ $DESTDIR2/$DATE/$d \
|mail -s "Backup Summary `date +"%m/%d/%y %r"`" $EMAIL
done

# Email results of backup
if [ $? -eq 0 ]; then
mail -s "Backup: SUCCESS" $EMAIL << EOF
Your backup from $SRCDIR to $DESTDIR completed successfully.
EOF
else
mail -s "Backup: FAILURE" $EMAIL << EOF
Your backup from $SRCDIR to $DESTDIR did not complete.
EOF
fi

exit

Sorry to be so dense, but will the function keep the above from running if the destination directory or directories already exist. It doesn't appears so to me.

Diggy


All times are GMT -5. The time now is 09:13 PM.