LinuxQuestions.org
Help answer threads with 0 replies.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Newbie
User Name
Password
Linux - Newbie This Linux forum is for members that are new to Linux.
Just starting out and have a question? If it is not in the man pages or the how-to's this is the place!

Notices


Reply
  Search this Thread
Old 01-10-2013, 10:43 PM   #1
Sanderson
LQ Newbie
 
Registered: Jan 2013
Posts: 7

Rep: Reputation: Disabled
Question copy multiple files to multiple directories


i have a group of files located in 1 directory that i want to copy to multiple user directories in /home however nothing i do appears to be working any help would be appreciated

if it helps im trying to use this:

find /home -type d -exec cp Accounts.txt User.txt Work.txt /home/User/Documents/Master \;

Last edited by Sanderson; 01-10-2013 at 11:26 PM. Reason: Adding in code
 
Old 01-10-2013, 11:54 PM   #2
divyashree
Senior Member
 
Registered: Apr 2007
Location: Bangalore, India
Distribution: RHEL,SuSE,CentOS,Fedora,Ubuntu
Posts: 1,386

Rep: Reputation: 135Reputation: 135
Quote:
Originally Posted by Sanderson View Post
i have a group of files located in 1 directory that i want to copy to multiple user directories in /home however nothing i do appears to be working any help would be appreciated

if it helps im trying to use this:

find /home -type d -exec cp Accounts.txt User.txt Work.txt /home/User/Documents/Master \;



You can use cp for multiple sources but not for multiple destination. You have to apply exec multiple times for multiple directories.
Here 2 dirs Master1 Master2. try this.

Code:
find /home -exec cp Accounts.txt User.txt Work.txt /home/User/Documents/Master1 \; -exec cp Accounts.txt User.txt Work.txt /home/User/Documents/Master2 \;
Or you can do this using loop in a shell script. Google this.

Last edited by divyashree; 01-10-2013 at 11:58 PM.
 
Old 01-11-2013, 12:01 AM   #3
shivaa
Senior Member
 
Registered: Jul 2012
Location: Grenoble, Fr.
Distribution: Sun Solaris, RHEL, Ubuntu, Debian 6.0
Posts: 1,800
Blog Entries: 4

Rep: Reputation: 286Reputation: 286Reputation: 286
You can create a script. Create a list of files to be moved and then do as:-
Code:
#!/bin/bash
flist=/tmp/fileslist   # Contains a list of files, one file in each line
for file in $(cat $flist)
do
echo -n "Enter destination dir. for $file: "; read destdir
cp $file $destdir
done
 
Old 01-11-2013, 12:08 AM   #4
Sanderson
LQ Newbie
 
Registered: Jan 2013
Posts: 7

Original Poster
Rep: Reputation: Disabled
This didnt work ill try to be more clear
I have 3 files called Accounts.txt, User.txt and Work.txt located in a directory called Master, the file path to the files is home/user/Documents/Master

I want to copy these 3 files to several user directories in /home.

The script im using dosent work.
 
Old 01-11-2013, 12:09 AM   #5
Sanderson
LQ Newbie
 
Registered: Jan 2013
Posts: 7

Original Poster
Rep: Reputation: Disabled
Thanks ill try this
 
Old 01-11-2013, 12:16 AM   #6
Sanderson
LQ Newbie
 
Registered: Jan 2013
Posts: 7

Original Poster
Rep: Reputation: Disabled
#!/bin/bash
flist=/tmp/fileslist # Contains a list of files, one file in each line
for file in $(cat $flist)
do
echo -n "Enter destination dir. for $file: "; read destdir
cp $file $destdir
done

that managed to copy the files into the home directory but not into the subdirectories within
 
Old 01-11-2013, 12:36 AM   #7
shivaa
Senior Member
 
Registered: Jul 2012
Location: Grenoble, Fr.
Distribution: Sun Solaris, RHEL, Ubuntu, Debian 6.0
Posts: 1,800
Blog Entries: 4

Rep: Reputation: 286Reputation: 286Reputation: 286
You just need to modify paths according to your requirement. First of all use absolute i.e. full path of files in /tmp/fileslist, not just file names, as:
Code:
~$ cat /tmp/fileslist
/home/user/Documents/Master/Accounts.txt
/home/user/Documents/Master/User.txt
/home/user/Documents/Master/Work.txt
And while running the script, enter full path of dest. dir. as /home/user1/, not just user1. Script would look like:
Code:
#!/bin/bash
flist=/tmp/fileslist   # Contains a list of files, one file in each line
cd /home/user/Documents/Master # You can use this line if /tmp/fileslist contains only file names, not their full path
for file in $(cat $flist)
do
echo -n "Enter destination dir. for $file: "; read destdir
cp $file $destdir
done
 
Old 01-11-2013, 12:41 AM   #8
Sanderson
LQ Newbie
 
Registered: Jan 2013
Posts: 7

Original Poster
Rep: Reputation: Disabled
Thanks i got it work, id just misspelt my file names
 
Old 01-11-2013, 10:29 AM   #9
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Arch + Xfce
Posts: 6,852

Rep: Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037
Aargh. No, no, no! Do Not Read Lines With For!!!!

(Not to mention the Useless Use Of Cat)

The proper way to operate on the output of a file or a command is to run a while+read loop on it (with null delimiters, if the input command supports it).

Code:
while read -r fname; do

	for dname in directory1 directory2 directory3; do
		cp "$fname" "dname"
	done

done <infile.txt
How can I read a file (data stream, variable) line-by-line (and/or field-by-field)?
http://mywiki.wooledge.org/BashFAQ/001

On the other hand, if your files can be targeted by a globbing pattern, then you should use a for loop.


And here's some info on how to properly use find:
http://mywiki.wooledge.org/UsingFind
http://www.grymoire.com/Unix/Find.html
 
Old 01-11-2013, 11:55 AM   #10
shivaa
Senior Member
 
Registered: Jul 2012
Location: Grenoble, Fr.
Distribution: Sun Solaris, RHEL, Ubuntu, Debian 6.0
Posts: 1,800
Blog Entries: 4

Rep: Reputation: 286Reputation: 286Reputation: 286
Quote:
Originally Posted by David the H. View Post
Aargh. No, no, no! Do Not Read Lines With For!!!!
Hello David,
Thanks for the explaination.

Well I had also tried with while+read, but because of this, the read statement which I used inside the script for taking dest. dir. name as input, was not working. Have a look:-
Code:
#!/bin/bash
flist=/tmp/fileslist
while read -r file
do
echo -n "Enter destination dir. for $file: "; read destdir  ## Problem was here in this read statement
cp $file $destdir
done < $flist
Is there any better way, so both read statements work? And how to take user's input using echo-read when using it in a while-read loop?
 
Old 01-18-2013, 05:10 AM   #11
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Arch + Xfce
Posts: 6,852

Rep: Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037
Sorry for the late reply. Nowadays I only have a chance to get back here every few days.

Yes, to handle multiple inputs you should generally use file descriptors, at least for the files.

file descriptors
redirections
file descriptors explained


The final link has one of the best descriptions of it that I've found so far.

Code:
#!/bin/bash

#replace the simple variable with a file descriptor.
exec {flist}</tmp/fileslist

while read -r -u "$flist" fname; do

    echo "Enter destination dir. for $fname,"
    echo "or type 'abort' to cancel the operation."

    while true; do

        IFS='' read -r -e -p 'Directory name: ' destdir
	echo

	if [[ ${destdir,,} == abort* ]]; then

            echo "Copying cancelled."
            break

        elif [[ -d $destdir && -w $destdir ]]; then

            echo "Copying $fname to $destdir."
            cp "$fname" "$destdir" || echo "Copy failed for some reason."
            break

        else

            echo "Not a valid directory.  Try again."

        fi

    done

done

#close out the fd again, if needed.
exec {flist}<&-
The $flist variable will be set to an automatically assigned file descriptor number that points to the actual file (generally 10 or higher). Then read's -u option can be used to tell it to take its input directly from the file. Thus the second read can be used to query the user unhindered.

The ability to have it automatically assign an fd to a variable is available from bash 4.1. If you're using an earlier version, then simply replace "{flist}/$flist" with a simple number. Don't use 0-2 as those are stdin/stdout/stderr, and bash sometimes uses 5 internally, so best to avoid it, but 3-4 and 6+ are usually safe.


Notice a few other changes I made too:

1) First, always quote your variables.

http://mywiki.wooledge.org/Arguments
http://mywiki.wooledge.org/WordSplitting
http://mywiki.wooledge.org/Quotes

This is a vitally important scripting point, so be sure to learn it well!

It's also recommended to avoid using variable names that are the same as valid command or keyword names, such as "file".

2) Always use the -r option with read, so that it doesn't try to expand backslash characters in the input. I also cleared the IFS variable for the user query one, since by default it would remove leading and trailing space from the input, although it might not be particularly necessary in this case. -p can supply a prompt instead of "echo -n", and finally, I added the -e option so that it will use the readline library for input. This gives the user the full set of shell editing features, and tab-completion on filenames.

3) Finally, I included a test to confirm that the input is actually a proper directory before it tries to copy the file there. It's always a good idea to ensure that user input is sane before trying to use it. Notice also how I put it in a continuous loop, so that it continues to ask until it succeeds, along with an "abort" option to escape from it if desired.

Last edited by David the H.; 01-18-2013 at 05:17 AM. Reason: minor rewording for clarity
 
1 members found this post helpful.
Old 01-18-2013, 10:17 AM   #12
shivaa
Senior Member
 
Registered: Jul 2012
Location: Grenoble, Fr.
Distribution: Sun Solaris, RHEL, Ubuntu, Debian 6.0
Posts: 1,800
Blog Entries: 4

Rep: Reputation: 286Reputation: 286Reputation: 286
Very well explained David.
For this, you deserves 2 reputations
 
Old 01-19-2013, 05:26 PM   #13
cbtshare
Member
 
Registered: Jul 2009
Posts: 645

Rep: Reputation: 42
Quote:
Originally Posted by David the H. View Post
Sorry for the late reply. Nowadays I only have a chance to get back here every few days.

Yes, to handle multiple inputs you should generally use file descriptors, at least for the files.


The $flist variable will be set to an automatically assigned file descriptor number that points to the actual file (generally 10 or higher). Then read's -u option can be used to tell it to take its input directly from the file. Thus the second read can be used to query the user unhindered.

The ability to have it automatically assign an fd to a variable is available from bash 4.1. If you're using an earlier version, then simply replace "{flist}/$flist" with a simple number. Don't use 0-2 as those are stdin/stdout/stderr, and bash sometimes uses 5 internally, so best to avoid it, but 3-4 and 6+ are usually safe.


Notice a few other changes I made too:

1) First, always quote your variables.

http://mywiki.wooledge.org/Arguments
http://mywiki.wooledge.org/WordSplitting
http://mywiki.wooledge.org/Quotes

This is a vitally important scripting point, so be sure to learn it well!

It's also recommended to avoid using variable names that are the same as valid command or keyword names, such as "file".

2) Always use the -r option with read, so that it doesn't try to expand backslash characters in the input. I also cleared the IFS variable for the user query one, since by default it would remove leading and trailing space from the input, although it might not be particularly necessary in this case. -p can supply a prompt instead of "echo -n", and finally, I added the -e option so that it will use the readline library for input. This gives the user the full set of shell editing features, and tab-completion on filenames.

3) Finally, I included a test to confirm that the input is actually a proper directory before it tries to copy the file there. It's always a good idea to ensure that user input is sane before trying to use it. Notice also how I put it in a continuous loop, so that it continues to ask until it succeeds, along with an "abort" option to escape from it if desired.
I'm not seeing much difference between using fd's and two variables though.This is just another way to do it I guess cuz it looks cool?

also for the read portion of the script since your calling back the fd, wouldnt you do it like this
Quote:
exec 4</tmp/filelist
while read -r -u "<4&" fname; do
when I do the above call it just hangs, but #ls <4& , works, so why wouldnt it read in the same way.

Last edited by cbtshare; 01-19-2013 at 06:33 PM.
 
Old 01-20-2013, 02:46 PM   #14
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Arch + Xfce
Posts: 6,852

Rep: Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037
Quote:
Originally Posted by cbtshare View Post
I'm not seeing much difference between using fd's and two variables though.This is just another way to do it I guess cuz it looks cool?
Sorry, I'm not sure what you mean by "two variables". In shivaa's problem above, the while loop was taking its input from the file, and the nested read inherited that same input from the parent. You need to redefine the stdin of at least one of the reads so that has a different input.

Or are you perhaps talking about the use of the automatic "{var}" fd designation? If so, then no, there isn't any real difference between it and a hard-coded fd, except that letting the shell do all the dirty work is more reliable in complex situations.


Quote:
also for the read portion of the script since your calling back the fd, wouldnt you do it like this
Code:
exec 4</tmp/filelist
while read -r -u "<4&" fname; do
when I do the above call it just hangs, but #ls <4& , works, so why wouldnt it read in the same way.
I see several problems here. First, the -u option takes the number of a previously set fd as its argument. Only the number. What you gave it would be unrecognized.

Second, your redirection syntax is wrong. The number of the fd that you're setting up comes first (defaults to 0 or 1 if left off, depending on direction), then the redirection arrow, and finally the target of it (Using "&n" to reference another fd). And it can't be quoted.

To make it a valid redirection you'd use "<&4", (input to fd0 from the currently-set target of fd4, which in this case is /tmp/filelist ).

Since you didn't give read a valid input, it just sits there listening on stdin for something to work on.


But third, even if you removed the -u and used the correct redirect, putting it there would have it apply to the read command only, not the loop as a whole. So each time the loop came back around it would re-establish the fd, and you end up reading the first line of the file over and over again.

I hope that clears it up a bit.


BTW, another working option would be to set up the stdin of the inner read to come from the terminal directly.

Code:
while read foo; do

   read -r bar </dev/tty
   echo "$foo : $bar"

done <infile.txt


Edit: "ls <4&" should only function at all if you happen to have a file named "4" in your PWD. Then it would set up stdin to be that file, "<4", and treat the "&" following it as a background fork. Then since ls doesn't read anything from stdin, it runs as if it has no arguments, lists the PWD, and exits.

If there's no file "4", then you should just get a "file not found" error for it.

Last edited by David the H.; 01-20-2013 at 02:58 PM.
 
Old 01-20-2013, 06:59 PM   #15
cbtshare
Member
 
Registered: Jul 2009
Posts: 645

Rep: Reputation: 42
Ok cool, I get your explanation why the read just sits there waiting,but you other :

Quote:
Edit: "ls <4&" should only function at all if you happen to have a file named "4" in your PWD. Then it would set up stdin to be that file, "<4", and treat the "&" following it as a background fork. Then since ls doesn't read anything from stdin, it runs as if it has no arguments, lists the PWD, and exits.

If there's no file "4", then you should just get a "file not found" error for it.
I dont find to be true, as at the cmd prompt I

>#exec 8</home/user
>#ls <&8
as well as

>#ls <4& works is I do exec 4</home/user
I browse the user directory.
I browse the directory of "user"

thus it sparked my question.

Annotation:
I also dont have a file name 4.

Last edited by cbtshare; 01-20-2013 at 07:11 PM.
 
  


Reply



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
Copy files from multiple directories into one directory MadRabbit Linux - Newbie 8 02-07-2014 07:56 PM
Bash Question(for loop): How to Zip multiple files under multiple directories Znrall Linux - General 2 08-01-2012 01:52 PM
Creating a script to move or copy files into multiple directories below the files matthes138 Linux - Newbie 5 08-25-2009 04:57 PM
find and copy files into multiple directories avargas22 Linux - Newbie 2 04-01-2004 11:11 AM

LinuxQuestions.org > Forums > Linux Forums > Linux - Newbie

All times are GMT -5. The time now is 03:08 PM.

Main Menu
Advertisement
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
Open Source Consulting | Domain Registration