[SOLVED] copy multiple files to multiple directories
Linux - NewbieThis 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
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
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
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
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.
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
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.
#!/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
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:
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
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
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?
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.
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
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.
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.
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.
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"
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.