copying multiple files to multiple directories

15,154

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
Share:
15,154

Related videos on Youtube

Luigi Tiburzi
Author by

Luigi Tiburzi

Updated on September 18, 2022

Comments

  • Luigi Tiburzi
    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
      kmacdonald almost 12 years
      do 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 GNU bash)).
  • Luigi Tiburzi
    Luigi Tiburzi almost 12 years
    Thank 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
    poige almost 12 years
    @LuigiTiburzi, actually you can keep find for another task, GNU's cp has -t key which stands for --target-directory=…
  • poige
    poige almost 12 years
    I just recommend using -r of xargs always (get a habit) since otherwise you can get rather unwanted things when there weren't input lines for xargs. Sometimes it would be just errors due to sanity checks built inside programs you running, but sometimes blunders.
  • poige
    poige almost 12 years
    Actually, I thought a bit and found that there's no need even to `-t': unix.stackexchange.com/a/40283/6622
  • Luigi Tiburzi
    Luigi Tiburzi almost 12 years
    Excuse 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'
    Gilles 'SO- stop being evil' almost 12 years
    @LuigiTiburzi Oops, I forgot to remove that. Edited.
  • Huygens
    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
    Huygens almost 12 years
    Cool answer, have to try it as I don't know the [1-5] syntax...