Thread: [Linuxcommand-discuss] how do you create a shell script with parameters like cp or mv?
Brought to you by:
bshotts
|
From: Mertens B. <bra...@li...> - 2002-08-03 10:48:45
|
Hi,
I would like to write a shell script that creates a backup of a source
directory to a destination directory.
However simply cp SRC DEST won't do, here's a brief description:
backup without parameters or options must copy the default SRC to the
default DEST. but I want to be able to use the script like backup SRC
DEST as well to create backups of other directories.
Here's why cp won't do:
I want the script to create a directory in DEST in the format (date
+'%Y%m%d') e.g. 20020703 and then copy all files in SRC recursively into
that directory.
But I want to be able to create more than one backup a day if that's
necessary, and I want the script to detect it has already created a
backup and add a number to the name of the dir so it would be like
20020703-2. So it would have to check recursively until the directory
doesn't exist and then create and copy.
I only started learning about scripts yesterday, so this might be a
little over my head, but I want to give it a try nonetheless! :) (I can
see you laughing at me already) :)
I downloaded and installed the lc_new_script script but if I understand
it right I can only do what I want like "backup -s SRC -d DEST" i.e.
with options, is it possible to do it without those or is that something
exclusive for cp and mv?
Here's what I've got so far, it isn't near what I want but it's fun
already!
SOURCE_DIR=~/scripts/
TARGET_DIR=~/scripts-backup
DATE=$(date +'%Y%m%d')
if [ -d ${TARGET_DIR}/${DATE} ]; then
echo "you already created a backup today!"
else
mkdir -p $TARGET_DIR/$DATE
cp -r $SOURCE_DIR/* $TARGET_DIR/$DATE
fi
Here are a couple of things I noticed already:
if you define the above variables like SOURCE_DIR="~/scripts/" the ~
doesn't get substituted.
Also, but I might have to reread the "writing shell scripts" tutorial
again, why do you sometimes need $VAR and sometimes ${VAR} and on other
occasions $(VAR). the last one seems to be for expressions, am I right?
Thanks a lot in advance, I loved the tutorial!
--
| Mertens Bram "M8ram" <bra...@li...>
| Registered [Red Hat] Linux User # 249103 since Octobre 2000
| http://linux.be | http://www.redhat.com | http://counter.li.org
\____________________________
|
|
From: William S. <wes...@co...> - 2002-08-04 14:14:00
|
On Saturday 03 August 2002 06:48 am, you wrote:
> Hi,
>
> I would like to write a shell script that creates a backup of a source
> directory to a destination directory.
>
> However simply cp SRC DEST won't do, here's a brief description:
>
> backup without parameters or options must copy the default SRC to the
> default DEST. but I want to be able to use the script like backup SRC
> DEST as well to create backups of other directories.
>
> Here's why cp won't do:
> I want the script to create a directory in DEST in the format (date
> +'%Y%m%d') e.g. 20020703 and then copy all files in SRC recursively into
> that directory.
> But I want to be able to create more than one backup a day if that's
> necessary, and I want the script to detect it has already created a
> backup and add a number to the name of the dir so it would be like
> 20020703-2. So it would have to check recursively until the directory
> doesn't exist and then create and copy.
>
> I only started learning about scripts yesterday, so this might be a
> little over my head, but I want to give it a try nonetheless! :) (I can
> see you laughing at me already) :)
Actually, I think you are doing pretty well for just one day.
>
> I downloaded and installed the lc_new_script script but if I understand
> it right I can only do what I want like "backup -s SRC -d DEST" i.e.
> with options, is it possible to do it without those or is that something
> exclusive for cp and mv?
No, cp and mv are not special. While new_script encourages the use of named
options, there is nothing preventing the use of an argument list instead.
>
> Here's what I've got so far, it isn't near what I want but it's fun
> already!
>
> SOURCE_DIR=~/scripts/
> TARGET_DIR=~/scripts-backup
> DATE=$(date +'%Y%m%d')
>
> if [ -d ${TARGET_DIR}/${DATE} ]; then
> echo "you already created a backup today!"
> else
> mkdir -p $TARGET_DIR/$DATE
> cp -r $SOURCE_DIR/* $TARGET_DIR/$DATE
> fi
This is good so far.
To eliminate the need for the "hard-coded" values in SOURCE_DIR and
TARGET_DIR, you would use positional parameters like so:
#!/bin/bash
SOURCE_DIR=$1
TARGET_DIR=$2
PROGNAME=$(basename $0) # Figure out name of program
DATE=$(date +'%Y%m%d')
# Check for arguments
if [ $# -ne 2 ]; then
echo "Usage: $PROGNAME source_dir destination_dir" 1>&2
exit 1
fi
# Check that source directory exists
if [ ! -d $SOURCE_DIR ]; then
echo "Source directory $SOURCE_DIR does not exist!" 1>&2
exit 1
if
# Perform the copy
if [ -d ${TARGET_DIR}/${DATE} ]; then
echo "you already created a backup today!"
else
mkdir -p $TARGET_DIR/$DATE
cp -r $SOURCE_DIR/* $TARGET_DIR/$DATE
fi
>
> Here are a couple of things I noticed already:
> if you define the above variables like SOURCE_DIR="~/scripts/" the ~
> doesn't get substituted.
Good observation.
>
> Also, but I might have to reread the "writing shell scripts" tutorial
> again, why do you sometimes need $VAR and sometimes ${VAR} and on other
> occasions $(VAR). the last one seems to be for expressions, am I right?
The difference between $VAR and ${VAR} is subtle. Imagine this:
You have a file called my_file1 and you want to copy it to my_file2, and you
start to write a general script for this task like so:
BASEFILE=my_file
cp $BASEFILE1 $BASEFILE2
Thinking about this, you see that this won't work because the shell will
think that BASEFILE1 and BASEFILE2 are seperate variable names. To fix this,
you would do it this way:
BASEFILE=my_file
cp ${BASEFILE}1 ${BASEFILE}2
So, you use {} whenever you need to eliminate ambiguity.
$(VAR) should really be $(command) since this performs command substitution,
that is, the results of the command are substituted.
>
> Thanks a lot in advance, I loved the tutorial!
You're welcome. Glad it was of use.
By the way, the next lesson I plan to write will cover positional parameters,
so stay tuned.
--
||||| William Shotts, Jr. (bshotts AT panix DOT com)
||||| Be a Linux Commander! Follow me to http://linuxcommand.org
|
|
From: Mertens B. <bra...@li...> - 2002-08-06 21:11:53
|
On Sun, 2002-08-04 at 16:12, William Shotts wrote:
> To eliminate the need for the "hard-coded" values in SOURCE_DIR and
> TARGET_DIR, you would use positional parameters like so:
>
> #!/bin/bash
>
> SOURCE_DIR=$1
> TARGET_DIR=$2
> PROGNAME=$(basename $0) # Figure out name of program
> DATE=$(date +'%Y%m%d')
>
[snip]
>
> if [ -d ${TARGET_DIR}/${DATE} ]; then
> echo "you already created a backup today!"
> else
> mkdir -p $TARGET_DIR/$DATE
> cp -r $SOURCE_DIR/* $TARGET_DIR/$DATE
> fi
I was just playing around with this a bit and I changed it to:
else
echo "mkdir -p ${TARGET_DIR}"
echo "tar -czf ${TARGET_DIR}/${DATE}.tar.gz ${SOURCE_DIR}"
fi
But I believe this introduces a problem: (the script is called bu)
if the user executes the command $ bu oldvcards/ backup/vcards
the result is:
mkdir -p backup/vcards
tar -czf backup/vcards/20020806.tar.gz oldvcards/
which what I want but if the user executes $ bu oldvcards/
backup/vcards/ the result is:
mkdir -p backup/vcards/
tar -czf backup/vcards//20020806.tar.gz oldvcards/
Won't the double slash cause a problem?
If it does the script should perform a regular expression on the
variables, the SOURCE_DIR won't make a difference but the TARGET_DIR
should be checked, would this work:
if [ ${TARGET_DIR} = .*/$ ]
echo "please omit the trailing '/' from ${TARGET_DIR}"
fi
Is it possible to strip that character automatically? I can only find
the 'offset' substring but that seems to work only to omit the beginning
characters... In JavaScript it is possible to determine the length of a
string, if that is possible in bash I could use the offset command...
TIA
--
| Mertens Bram "M8ram" <bra...@li...>
| Registered [Red Hat] Linux User # 249103 since Octobre 2000
| http://linux.be | http://www.redhat.com | http://counter.li.org
\____________________________
|
|
From: William S. <wes...@co...> - 2002-08-04 18:32:06
|
On Sunday 04 August 2002 11:59 am, you wrote:
> On Sun, 2002-08-04 at 16:12, William Shotts wrote:
> > No, cp and mv are not special. While new_script encourages the use of
> > named options, there is nothing preventing the use of an argument list
> > instead.
>
> let me see if I understand this correctly: if you don't specify the
> options like the script does, you can still "extract" them from the
> command as the $1, $2 and so on?
> So every WORD that is passed on with the command that is no named option
> will get stored in some sort of an array?
Yes, this is correct. Actually, this is about the only way to get anything
from the command line. The scripts I write for using new_script use the
getopts command to process options, but the lesson on positional parameters
will demonstrate how to do it with a while, a case, and a bunch of shifts.
It is actually a more flexible way to do it, and I will probably change
new_script to do it that way.
>
> I've been reading the Bash man pages but the part about the $ was very
> unclear to me...
That's why it needs its own lesson ;-)
>
> > To eliminate the need for the "hard-coded" values in SOURCE_DIR and
> > TARGET_DIR, you would use positional parameters like so:
> >
> > #!/bin/bash
> >
> > SOURCE_DIR=$1
> > TARGET_DIR=$2
> > PROGNAME=$(basename $0) # Figure out name of program
> >
> > DATE=$(date +'%Y%m%d')
> >
> > # Check for arguments
> >
> > if [ $# -ne 2 ]; then
> > echo "Usage: $PROGNAME source_dir destination_dir" 1>&2
> > exit 1
> > fi
>
> So $# would be some loop going through the array of options? What does
> the "2" in the expression stand for?
No, $# holds the number of words on the command line in addition to $0
which the name of the command. The "2" is the number of arguments the
program requires (source and target directories). The if statement is
testing to see if the number of arguments is not equal (-ne) to 2. If it is
not equal, then output a usage message and exit with an error.
>
> > # Check that source directory exists
> >
> > if [ ! -d $SOURCE_DIR ]; then
> > echo "Source directory $SOURCE_DIR does not exist!" 1>&2
> > exit 1
> > if
> >
> > # Perform the copy
>
> In the mean time I think I'm going to have it tar -czf to save some
> space :), but the principle is the same...
>
> > if [ -d ${TARGET_DIR}/${DATE} ]; then
> > echo "you already created a backup today!"
> > else
> > mkdir -p $TARGET_DIR/$DATE
> > cp -r $SOURCE_DIR/* $TARGET_DIR/$DATE
> > fi
>
> The part about quoting is something I had to read again, it's ore
> important than I first thought, I've some experience with JavaScript and
> it's a lot less important there...
>
> > The difference between $VAR and ${VAR} is subtle.
> > So, you use {} whenever you need to eliminate ambiguity.
>
> Indeed, that part was quite clear in the bash man pages, that means I
> understood it the first time I read it! :))
>
> > You're welcome. Glad it was of use.
> >
> > By the way, the next lesson I plan to write will cover positional
> > parameters, so stay tuned.
>
> Definitely, I'm looking forward to the next lesson! I like it that the
> lessons are focused on the "non-root" tasks, it takes away much of the
> fear of "learning by example", which is something I really like.
>
> Actually related to this, I didn't perform the installation exactly the
> way you suggest in the README, to avoid exactly the kind of problems
> root permissions can cause. I cp'd the file to my ~/bin directory and
> changed its permissions to 700. Also I removed the -f from the rm
> commands in the scripts, I know the only file that get deleted are in
> the /tmp folder but still. (It actually was the first time I exercised
> my rights to modify free software, that may also have been part of the
> reason! :)) )
>
> Why is the -f there anyway? I don't get any questions since I have full
> rights to those files...
-f does two things:
1. If the file you are trying to delete does not exist, -f will force rm to
not complain about it.
2. -f will override -i. This is useful if, for example, you are on a Red Hat
system and rm has been aliased to "rm -i" like it is on the root account.
This way you can delete multiple files without being prompted before each one
is deleted.
>
> Thanks for the reply, I'll keep you posted about my progress (though it
> may be slow)
>
> Bram
--
||||| William Shotts, Jr. (bshotts AT panix DOT com)
||||| Be a Linux Commander! Follow me to http://linuxcommand.org
|