-1

I have the following folder dtructure:

|-/dataa/dataA/Content1/*.txt
|-/dataa/dataA/Content2/*.txt
...
|-/dataz/dataZ/Content1/*.txt
|-/dataz/dataZ/Content2/*.txt

And I want to copy/move all the *.txt from deep in the folder hierarchy to respective Content1 and Content2 folders in the current working directory? I have tried:

$ find . -name Content1 -exec cp *.txt ./Content1 \;
cp: cannot stat '*.txt': No such file or directory
cp: cannot stat '*.txt': No such file or directory
cp: cannot stat '*.txt': No such file or directory
cp: cannot stat '*.txt': No such file or directory
cp: cannot stat '*.txt': No such file or directory
cp: cannot stat '*.txt': No such file or directory
cp: cannot stat '*.txt': No such file or directory
cp: cannot stat '*.txt': No such file or directory
cp: cannot stat '*.txt': No such file or directory
cp: cannot stat '*.txt': No such file or directory
cp: cannot stat '*.txt': No such file or directory

Tried RedGrittyBrick's solution but unfortunately the count of files do not tally.

$ find . -name CameraRGB -print|xargs ls -l|wc -l
10029
$ mkdir CameraRGB_consolidated
$ find . -name CameraRGB -exec sh -c 'cp "1ドル"/*.png CameraRGB_consolidated' sh {} \;
$ ll CameraRGB_consolidated/|wc -l
5003
asked Oct 29 at 9:18
3
  • My data source has 3-level depth. dataa/dataA/CameraRGB/*.png Commented Oct 29 at 12:01
  • I suspect that RedGrittyBrick's solution may overwrite a file copied earlier with a file copied later (from another directory), if the names are identical. Commented Oct 29 at 12:03
  • My bad. That's right. All the subfolders have some same file names. Commented Oct 29 at 12:04

2 Answers 2

2
$ mkdir -p a/b/c/Content1
$ echo aaa > a/b/c/Content1/aaa.txt
$ mkdir Content1
$ tree
.
├── a
│  └── b
│  └── c
│  └── Content1
│  └── aaa.txt
└── Content1
6 directories, 1 file
 
$ find a -name Content1 -exec sh -c 'cp "1ドル"/*.txt Content1' sh {} \;
$ tree
.
├── a
│  └── b
│  └── c
│  └── Content1
│  └── aaa.txt
└── Content1
 └── aaa.txt
6 directories, 2 files

Things to observe:

  • You need the placeholder {} because find doesn't change the current working directory for you.

  • You need to protect *.txt from globbing by the shell when interpreting the initial find command

  • You want a shell to glob *.txt when the cp command is being executed.

  • It is prudent to exclude ./Content1 by, for example, specifying something other than . as the start point for find. Otherwise you may end up asking find to copy or move a file onto itself. I would change the name of ./Content1 and rename it after all the copies/moves were complete.

  • Never embed {} in the shell code. which also has some tips about making this more efficient.

  • What is the second sh in `sh -c 'some shell code' sh`?

  • https://www.gnu.org/software/bash/manual/bash.html#Invoking-Bash

     -c If the -c option is present, then commands are read from the
     first non-option argument command_string. If there are argu‐
     ments after the command_string, the first argument is as‐
     signed to 0ドル and any remaining arguments are assigned to the
     positional parameters. The assignment to 0ドル sets the name of
     the shell, which is used in warning and error messages.
    
  • use cp -p if file date etc need to be preserved.

  • as per comments to question, if any files have the same name as another in a different folder, you will overwrite in the target folder. Using a noclobber option may guard against this going unnoticed but it may be preferable to add an index number as filename suffix.

  • if you are anything like me, you may begin to wonder if it would be simpler and somehow more rational to reach for your favourite programming language (assuming that isn't already bash and find).

answered Oct 29 at 10:27
7
  • What does sh {} do there? Commented Oct 29 at 11:06
  • OK. It explains the second sh is just an arbitrary string for error/warning message. But why sh instead of any other string so that it doesn't confuse me that it is meant to execute the evaluation/expansion of {}? And why does that follow with {} at all? Commented Oct 29 at 11:23
  • @khteh: It may help to man bash and search for the section on -c Commented Oct 29 at 11:29
  • I am not sure if your command works. Check my post. The total count of the files and the destination folder do not tally. Commented Oct 29 at 11:41
  • @kteh I just checked it all a second time, I actually ran the commands I show above. They worked. Which post do you mean? Commented Oct 29 at 11:47
1

You can use rsync if you have it installed:

# Scenario setup
mkdir -p dataaa/dataA/Content{1,2}
mkdir -p dataaz/dataZ/Content{1,2}
touch dataaa/dataA/Content{1,2}/{a1,a2,a3}.txt
touch dataaz/dataZ/Content{1,2}/{z1,z2,z3}.txt
# Copy the Content* directories and their contents to the current directory
rsync --dry-run -vrtR data*/*/./Content* .

Remove the --dry-run when you're happy it's doing what you want.

The -rt flags tell rsync to copy files and directories recursively. The -v flag enables verbose output so you can see what's happening. The -R flag tells rsync to copy including relative paths, and in this case I've used /./ in the source path to indicate the base of the path to be copied to the destination.

answered Oct 29 at 13:17

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.