问题
My application X organizes projects in folders with a specific tree. The first level of the tree is defined (hard constraint), while all the contents are freely organized.
My goal is to iterate over all folders of the project tree, parse folder names to get targets based on conditions, then copy the selected targets to a new project folder, keeping the tree structure, so that application X is not going to complain.
So far, I've come up with this:
cp -pr `find . -type d | grep -i targetstring` ../newProjectFolder/
This has issues: 1) it doesn't preserve folder tree; 2) throws a lot of errors cp: cannot stat (see also this). It isn't just working as intended.
Moreover, parsing the output of find is generally a bad idea.
I was able to overcome first problem with --parents as suggested here. However, I'm copying way less than what I need due to point 2, so not a solution after all.
I am looking for a one-liner solution to the problem. Script based solution is available here. EDIT: as this was marked as duplicated of the link provided, I'll rephrase a little: I want a solution where I don't need to explicitly while and if-else my way to it. Piping find and grep provides all the locations to be copied, but cp is complaining. How do we feed them to cp, without it complaining?
I was looking for something using -exec as suggersted here, and came up with the following, which is not working (nothing gets copied) (only partial tree structure is copied, and files inside that structure are only copied if they match the regex as well):
find . -regex ".*targetstring.*" -exec cp -rp --parents \{\} ../newProjectFolder \;
回答1:
"one-liner" but sed will not work for newlines $'\n' in filename
assuming you cd into project folder and run find . always with .
it will match targetstring (on any level) and will copy the whole (containing) tree from first level
example treesearchProjectFolder/some/parent/folders/matched/targetstring/foo/bar/
a search for *targetstring* inside searchProjectFolder/ will copy whole ./some folder (including all files and subfolders)
while -regex or -ipath will print all files, -iname will only print the matching folder targetstring/ itself. you can limit search to specific level(s) with -mindepth 5 and -maxdepth 5, and specify the target type to match folders only with -type d (but let us take away this options for now)
create newProjectFolder and run find on example tree
mkdir newProjectFolder
cd searchProjectFolder
find . -iname "*targetstring*"
result on example tree is
./some/parent/folders/matched/targetstring
now find result is piped to grep so we can cut the string to first level only
find . -iname "*targetstring*" | grep -o ^\./[^/]*
result on example tree is
./some
instead of grep we can use sed to quote the "folder name" (may have spaces)
find . -iname "*targetstring*" | sed -n 's,^\(\./[^/]*\).*,"\1",p'
result on example tree is
"./some"
finally let's cp all folders with eval because "folder names" are quoted. this is the "one-liner":
eval cp -a $(find . -iname "*targetstring*" | sed -n 's,^\(\./[^/]*\).*,"\1",p') ../newProjectFolder
result on example tree is
cp -a "./some" ../newProjectFolder
for better understanding i will explain the sed
sed -n 's,^\(\./[^/]*\).*,"\1",p'
sed -n 's, ^ \( \. / [^/]* \) .* , "\1" ,p'
-n = do not print input
p in ,p' = print only matching output
\1 = print the first capture group\( = begin of capture group\) = end of capture group
^ = match begin of string only\. = single dot (.)/ = single slash (path delimiter)[^/] = any single byte, but not a slash[^/]* = any string not containing slashes. = single byte.* = any string
so the (first) capture group is (begin of line)./example string
the capture group is quoted ("\1")"./example string"
edit:
another "real" one-liner if you only want to copy folder targetstring/ (with parent tree)
mkdir newProjectFolder
cd searchProjectFolder
find . -iname "*targetstring*" -exec cp -a --parents {} ../newProjectFolder \;
result on example tree is
cp -a --parents "./some/parent/folders/matched/targetstring" ../newProjectFolder
来源:https://stackoverflow.com/questions/61104639/recursive-copy-files-in-bash-preserving-dir-tree