LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices

Reply
 
Search this Thread
Old 06-05-2012, 09:49 AM   #1
Sandust
LQ Newbie
 
Registered: Jun 2012
Posts: 2

Rep: Reputation: Disabled
Repeat similar thing in Bash


I have a script that copies files from one source to 1 to 4 destinations. The user tells the script where the source is and how many destinations, then where the destinations are.

I have everything working just fine, but repeat the code for the copies four times. One set of code for one destination, another set for two destination ... a fourth set for four destinations.

Is there a way to simplify this code?

Code:
copy1 ()
	{
	cp -Rp $SOURCEPATH $COPYPATH1/
	wait
	}

copy2 ()
	{
	cp -Rp $SOURCEPATH $COPYPATH1/
	wait
	cp -Rp $COPYPATH1 $COPYPATH2/ &
	wait
	}

copy3 ()
	{
	cp -Rp $SOURCEPATH $COPYPATH1/ 
	wait
	cp -Rp $COPYPATH1 $COPYPATH2/ &
	cp -Rp $COPYPATH1 $COPYPATH3/ &
	wait
	}

copy4 ()
	{
	cp -Rp $SOURCEPATH $COPYPATH1/
	wait
	cp -Rp $COPYPATH1 $COPYPATH2/ &
	cp -Rp $COPYPATH1 $COPYPATH3/ &
	cp -Rp $COPYPATH1 $COPYPATH4/ &
	wait
	}
Notice that the second through fourth copies are in parallel and after first copy has completed.

Is there a way to do this with one for loop?



Thanks,
Dusty

Last edited by Sandust; 06-05-2012 at 03:19 PM.
 
Old 06-05-2012, 10:49 AM   #2
OdinnBurkni
Member
 
Registered: Feb 2007
Location: Iceland
Distribution: Fedora 14, CentOS, FreeNAS
Posts: 126

Rep: Reputation: 20
This may not be the most beautiful solution and maybe it's too complicated but it seems to get the job done...
You need one file for source path and another for the copy path/s
Then you have one file to host it all.
The script to read the source and destination paths:
Code:
#!/bin/bash
# This to clear out the contents of the file in the beginning. Just change paths to what you want to use
cat /dev/null > /home/script.sh
# Some variants
source="/home/source.txt"
dest="/home/copy.txt"

for ent in `cat $source`
        do
        read -a SOURCEPATH <<< "$ent"
done

for entry in `cat $dest`
        do
        read -a COPYPATH <<< "$entry"
        echo "cp -Rp $SOURCEPATH $COPYPATH"  >> /home/script.sh
done
This will make a file similiar to this one:
Code:
cp -Rp /source/file.txt /destination/path/1
cp -Rp /source/file.txt /destination/path/2
If you have more than one destination line then you'll have more than one line in the result file.
Then you just bash the script.sh and it should copy from source to all destinations....
Hope this helps.
 
Old 06-05-2012, 01:09 PM   #3
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942Reputation: 942
Here is what I'd do. First, build an array of destinations (target directories), and another of the things to copy to them (source files).

You could do that simply by assuming that all directories specified as parameters to the script are destinations, and all specified files are to be copied to all of them:
Code:
#!/bin/bash

source=()
target=()

for item in "$@" ; do

    if [[ -d "$item" ]]; then
        # $item is a directory
        target+=("$item")

    elif [[ -f "$item" ]]; then
        # $item is a file
        source+=("$item")

    elif [[ -e "$item" ]]; then
        # $item exists
        echo "$item: Not a file nor a directory." >&2
        exit 1

    else
        # $item does not exist
        echo "$item: No such file or directory." >&2
        exit 1
    fi
done
To do the actual copies, you only need to loop over all destinations:
Code:
for dest in "${target[@]}" ; do
    cp "${source[@]}" "$dest" || exit $?
done
However, to maximize the use of the page cache, it is best to copy each source file to all of the destinations, before moving to the next source file. That way, if your kernel has the memory to spare, it'll only read the source once:
Code:
for src in "${source[@]}" ; do
   for dest in "${target[@]}" ; do
       cp "$src" "$dest" || exit $?
   done
done
You could even do the copies in parallel, possibly speeding things up a small bit:
Code:
for src in "${source[@]}" ; do
   for dest in "${target[@]}" ; do
       cp "$src" "$dest" &
   done
   wait
done
Note that this latter one does not abort if a file cannot be copied, unlike the previous examples.

Last edited by Nominal Animal; 06-05-2012 at 01:11 PM.
 
Old 06-05-2012, 03:16 PM   #4
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian sid + kde 3.5 & 4.4
Posts: 6,823

Rep: Reputation: 1946Reputation: 1946Reputation: 1946Reputation: 1946Reputation: 1946Reputation: 1946Reputation: 1946Reputation: 1946Reputation: 1946Reputation: 1946Reputation: 1946
@Sandust: Please use ***[code][/code] tags*** around your code and data, to preserve formatting and to improve readability. Please do not use quote tags, colors, or other fancy formatting.


OdinnBurkni:
Code:
for ent in `cat $source`
Don't read lines with for!
$(..) is highly recommended over `..`

Last edited by David the H.; 06-05-2012 at 03:18 PM. Reason: added one
 
Old 06-06-2012, 11:41 AM   #5
Reuti
Senior Member
 
Registered: Dec 2004
Location: Marburg, Germany
Distribution: openSUSE 11.4
Posts: 1,319

Rep: Reputation: 252Reputation: 252Reputation: 252
If it’s limited to 4, maybe checking the name of the target is also suitable:
Code:
copy ()
	{
	cp -Rp $SOURCEPATH $COPYPATH1
	[[ $COPYPATH2 ]] && cp -Rp $COPYPATH1 $COPYPATH2 &
	[[ $COPYPATH3 ]] && cp -Rp $COPYPATH1 $COPYPATH3 &
	[[ $COPYPATH4 ]] && cp -Rp $COPYPATH1 $COPYPATH4 &
	wait
	}
Instead of knowing the number of destinations, the unused targets needs to be emptied instead.
 
Old 06-06-2012, 12:22 PM   #6
Sandust
LQ Newbie
 
Registered: Jun 2012
Posts: 2

Original Poster
Rep: Reputation: Disabled
Reuti,

what does this do?

Code:
[[ $COPYPATH4 ]] &&
If $COPYPATH4 exists then do the copy?


Thanks,
Dusty
 
Old 06-06-2012, 12:26 PM   #7
Reuti
Senior Member
 
Registered: Dec 2004
Location: Marburg, Germany
Distribution: openSUSE 11.4
Posts: 1,319

Rep: Reputation: 252Reputation: 252Reputation: 252
Yep. It’s in man bash section “[[ expression ]]” and “CONDITIONAL EXPRESSIONS”.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Bash: Repeat commands until a condition is matched iniuria Programming 4 03-09-2011 09:54 AM
Perl command that's similar to cut in bash? hawk__0 Programming 8 07-10-2010 02:37 AM
a little bash help with the 'for' command (or similar) checkmate3001 Linux - General 5 02-18-2009 12:52 PM
How to grep similar lines in bash? bruno buys Linux - Software 2 12-02-2005 11:56 PM
how-to: repeat OR iterate shell OR bash command delay OR interval admarshall Linux - General 5 07-18-2005 10:47 PM


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

Main Menu
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration