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
2 Answers 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
*.txtfrom globbing by the shell when interpreting the initialfindcommandYou want a shell to glob
*.txtwhen thecpcommand is being executed.It is prudent to exclude
./Content1by, 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./Content1and 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.
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 -pif 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
noclobberoption 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).
-
-
OK. It explains the second
shis just an arbitrary string for error/warning message. But whyshinstead 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?khteh– khteh2025年10月29日 11:23:29 +00:00Commented Oct 29 at 11:23 -
@khteh: It may help to
man bashand search for the section on-cRedGrittyBrick– RedGrittyBrick2025年10月29日 11:29:57 +00:00Commented 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.khteh– khteh2025年10月29日 11:41:35 +00:00Commented 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?RedGrittyBrick– RedGrittyBrick2025年10月29日 11:47:32 +00:00Commented Oct 29 at 11:47
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.
dataa/dataA/CameraRGB/*.png