How do I find all files in a folder and its subfolders terminated with 3 digits and move them to a new place while keeping the directory structure?
Alternatively, how can I find all files whose names don't end with three digits?
How do I find all files in a folder and its subfolders terminated with 3 digits and move them to a new place while keeping the directory structure?
Alternatively, how can I find all files whose names don't end with three digits?
Much cleaner solution, based on an answer linked by @don_crissti. (Rsync filter: copying one pattern only)
rsync -av --remove-source-files --include='*[0-9][0-9][0-9]' --include='*/' --exclude '*' /tmp/oldstruct/ /tmp/newstruct/
And the negation:
rsync -av --remove-source-files --exclude='*[0-9][0-9][0-9]' /tmp/oldstruct /tmp/newstruct/
Original answer:
This should do it. It will find any file in the structure you cd
into ending in 3 digits, create a destination folder in the /tmp/newstruct
, and move the file.
cd /tmp/oldstruct
find ./ -type f -regextype posix-basic -regex '.*[0-9]\\{3\\}' |
while read i; do
dest=/tmp/newstruct/$(dirname $i)
mkdir -vp $dest
mv -v $i $dest
done
I'd recommend prepending the mkdir
and mv
with echo
before you actually run it, just to ensure it does what you're expecting.
To negate the 3 digits, simply place do ! -regex
instead.
Here is a simpler method which relies upon rsync. However, it does call rsync
for every file it finds, so definitely not very efficient.
find ./ -type f -regextype posix-basic -regex '.*[0-9]\{3\}' --exec rsync -av --remove-source-files --relative {} /tmp/newstruct
--include
and --exclude
flags with rsync
.
– alienth
Sep 11 '15 at 19:29
./b: b001 bbb c
./b/c: c001 ccc $ find ./ -regextype posix-basic ! -regex '.*[0-9]{3}' -print | while read i; do d=/tmp/newstruct/$(dirname $i); mkdir -vp $d; mv -v $i $d; done mkdir: created directory ‘/tmp/newstruct’ mv: cannot move ‘./’ to ‘/tmp/newstruct/./.’: Device or resource busy ‘./b’ -> ‘/tmp/newstruct/./b’ mv: cannot stat ‘./b/c’: No such file or directory mv: cannot stat ‘./b/c/ccc’: No such file or directory mv: cannot stat ‘./b/bbb’: No such file or directory ‘./aaa’ -> ‘/tmp/newstruct/./aaa’ `
– wsdzbm Sep 11 '15 at 19:42-type f
to the find params.
– alienth
Sep 11 '15 at 19:44
-exec
because I couldn't easily think of a way to duplicate the structure. Specifically the parts where I'm fetching the dirname
and creating that directory. I could be missing something, tho.
– alienth
Sep 11 '15 at 20:05
find
finds the regexp in the full path but not the filename only? I found '.*[0-9]\{3\}'
matches filename like 111
and the output filename is ./111
– wsdzbm
Sep 11 '15 at 20:11
-type f
, the find will match directory names ending in 3 digits (not sure that is what you're referring to, tho).
– alienth
Sep 11 '15 at 20:15
-exec
solution which relies on rsync
and added it above. Although that's a lot of rsync
calls, potentially.
– alienth
Sep 11 '15 at 20:17
--include
, which isn't included in that answer. The rsync
docs don't seem to indicate that it supports quantifiers?
– alienth
Sep 11 '15 at 20:23
find
approach will fail if any of your file names contain newlines or glob characters. There's no need to loop over find'
s output, you can use -exec
instead.
– terdon
Sep 12 '15 at 10:31
You can do this with bash :
## Make ** match all files and 0 or more dirs and subdirs
shopt globstar
## Iterate over all files and directories
for f in **; do
## Get the name of the parent directory of the
## current file/directory
dir=$(dirname "$f");
## If this file/dir ends with 3 digits and is a file
if [[ $f =~ [0-9]{3} ]] && [ -f "$f" ]; then
## Create the target directory
mkdir -p targetdir1/"$dir"
## Move the file
mv "$f" targetdir1/"$f"
else
## If this is a file but doesn't end with 3 digits
[ -f "$f" ] &&
## Make the target dir
mkdir -p targetdir2/"$dir" &&
## Move the file
mv "$f" targetdir2/"$f"
fi
done