copying multiple files to multiple directories
Solution 1
echo dir[1-3] | xargs -r -n1 cp file[1-5]
Solution 2
You are making a small mistake in your usage of xargs. Without the -n parameter, xargs append all parameters in input to the command and echo dir{1..3}
will not add the necessary carriage return between each entry, so xargs will consider the result as one parameter.
xargs has an option -n which can limit the number of entry, and you need to list the directories with either a \n or \0 as separator:
find . -mindepth 1 -maxdepth 1 -type d -print0 | xargs -0 -n 1 cp file{1..5}
The find command will look for all directories in the current one and use the \0 as separator (which works better if you have directories with spaces) and xargs uses the \0 as separator and launch the command each time with only 1 parameter.
Solution 3
Something like the following should work if they truly have such names:
#!/bin/bash
shopt -s nullglob
for dir in dir*/; do
cp file* "$dir"
done
If you want to copy all files to all directories regardless of name:
#!/bin/bash
shopt -s nullglob dotglob
for dir in */; do
for file in *; do
[[ -f $file ]] && cp "$file" "$dir"
done
done
Solution 4
You're almost there. You need to run a separate cp
command for each destination directory, because cp
only makes one copy of every source file. The primary purpose of xargs
is to run one command over multiple arguments, but you can tell it to pass a single item at a time with the -n
option. Also {} ;
is the syntax of find … -exec
; you don't need (and can't use) it with xargs
.
echo dir{1..3} | xargs -n 1 cp file{1..5}
Note that this only works if your directory names do not contain any whitespace or any of the characters \'"
, because xargs
has no way to tell that those characters are part of file names and not quoting and separating input files. An alternative method that doesn't have any such problem is to use a shell loop:
for d in dirs{1..3}; do
cp file{1..5} "$d"
done
All this assumes that you can easily distinguish between files and directories by their names. If you can't, you can match directories by adding a /
at the end; for example */
matches all directories in the current directories, foo[0-9]*/
matches all directories whose name begins with foo
and a digit, etc. There is no similar way to match only non-directories in most shells, but if you pass directories to cp
with no argument to tell it to make a recursive copy, the directories are ignored with an error message.
for d in */; do
cp * "$d"
done
In zsh, you can use glob qualifiers to match only certain file types.
for d in *(/); do
cp *(.) $d
done
Related videos on Youtube
Luigi Tiburzi
Updated on September 18, 2022Comments
-
Luigi Tiburzi over 1 year
I've a problem copying many files in different directories. Let me explain better:
Let's say I have the following in a dir:
$ ls file1 file2 file3 file4 file5 dir1 dir2 dir3
and I want to copy every file* in every dir* with a single command. I've tried with:
echo dir{1..3} | xargs cp file{1..5} '{}' \;
My intent was to tell xargs to process every single dir* from echo and copy all the files in the input processed but this and similar didn't work. I would like to avoid the use of a script because it's a task I have to repeat for about 20 directories and the names of the files are slightly different so I'd prefer modifying a command rather than a script.
-
kmacdonald almost 12 yearsdo you want each directory to get a copy of all the files? (that's something for which you could iterate over the directories, doing the cp for each one (
for x in dir*; do cp file* $x; done
with GNUbash
)).
-
-
Luigi Tiburzi almost 12 yearsThank you! That was exactly what I was looking for: a solution with xargs. Although the syntax is really ugly it's what I wanted
-
poige almost 12 years@LuigiTiburzi, actually you can keep
find
for another task, GNU'scp
has-t
key which stands for--target-directory=…
-
poige almost 12 yearsI just recommend using
-r
ofxargs
always (get a habit) since otherwise you can get rather unwanted things when there weren't input lines forxargs
. Sometimes it would be just errors due to sanity checks built inside programs you running, but sometimes blunders. -
poige almost 12 yearsActually, I thought a bit and found that there's no need even to `-t': unix.stackexchange.com/a/40283/6622
-
Luigi Tiburzi almost 12 yearsExcuse me but when I run the first command you wrote I got this error: cp: cannot stat
{}': No such file or directory cp: cannot stat
;': No such file or directory cp: cannot stat{}': No such file or directory cp: cannot stat
;': No such file or directory cp: cannot stat{}': No such file or directory cp: cannot stat
;': No such file or directory as if it's not able to understand {} -
Gilles 'SO- stop being evil' almost 12 years@LuigiTiburzi Oops, I forgot to remove that. Edited.
-
Huygens almost 12 years@poige cool about the -r option. I did not know it. And yep you are right about the -t for cp, I thought of it too, but it does not really apply in this use case.
-
Huygens almost 12 yearsCool answer, have to try it as I don't know the [1-5] syntax...