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?

  • By “keeping the structure” do yo mean moving into subdirectories, recursively? – Incnis Mrsi Sep 11 '15 at 19:06
  • 1
    Unless I'm missing something, all files matching or not matching a pattern = all files. – don_crissti Sep 11 '15 at 19:14
  • @IncnisMrsi Yes, keep the directory tree, e.g. move /old/a/a001 to /new/a/a001, /old/a/b/b012 to /new/a/b/b012 – Lee Sep 11 '15 at 19:18
  • @don_crissti hehe... two independent cases they are. Maybe it's clearer to say "matching (or not matching)". In fact, what I wanna do now is to remove all files not ended with 3 digits, the same effect as moving all files ended with 3 digits and deleting all old files. – Lee Sep 11 '15 at 19:21
  • unix.stackexchange.com/q/2161 – don_crissti Sep 11 '15 at 19:48

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

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
  • The indent for while..done is necessary? – Lee Sep 11 '15 at 19:25
  • For this setup, yes. Although you might be able to find something that won't require loops by using a combination of --include and --exclude flags with rsync. – alienth Sep 11 '15 at 19:29
  • $ls -R .: a001 aaa b ./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’ – Lee Sep 11 '15 at 19:42
  • Ah, since you're negating, it's trying to move the directories as well. Add -type f to the find params. – alienth Sep 11 '15 at 19:44
  • I didn't notice the '!' ...... Perfect now!!! – Lee Sep 11 '15 at 19:47

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" 
        ## 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" 
  • @don_crissti ah, yes, you may well be right. The OP also said that all files are in a folder so I'm not sure what they want. And thanks, second command fixed. – terdon Sep 12 '15 at 11:14
  • No problem. I had initially wanted to vote to close it as a dupe of the other one but due to the fact the question is quite confusing I ended up leaving just a comment (although it's almost a duplicate imo). – don_crissti Sep 12 '15 at 11:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.