The command you've posted in the question
find . -type d -execdir bash -c "mkdir -p subdirectory_a && mkdir -p subdirectory_b && mv *_a.tif subdirectory_a 2>/dev/null && mv *_b.tif subdirectory_b 2>/dev/null" \;
behaves correctly ( that is , it does what it is supposed to do ). But what it spells out is not what you want.
The -execdir
argument operates such that "...the specified command is run from the
subdirectory containing the matched file," (source, find(1) )
That is to say, when your current working directly is root
and the command locates subdirectory1
it will not descend there as you expect. The subdirectory1
which matches -type d
was found when find
was inside root
, hence it will stay there and execute the mkdir
command specified from there. Naturally, there is no files inside root
, hence nothing will be moved around, but also note that mkdir -p
won't produce an error, as specified by the mkdir(1) : "no error if existing".
So how do we make this match what you want? Let's assume the same structure you've posted
$ tree /tmp/root
/tmp/root
├── subdir1
│ ├── file_a1_a.tif
│ ├── file_a1_b.tif
│ ├── file_a2_a.tif
│ └── file_a2_b.tif
└── subdir2
├── file_b1_a.tif
├── file_b1_b.tif
├── file_b2_a.tif
└── file_b2_b.tif
I'd suggest making it a three-step process. There is no need to make a one single command do all the work. Focus on the desired result rather than "code golf". So make the directories first
$ find /tmp/root -maxdepth 1 -mindepth 1 -exec bash -c 'mkdir ${1}/subdirectory_{a,b}' sh {} \;
$ tree
.
├── subdir1
│ ├── file_a1_a.tif
│ ├── file_a1_b.tif
│ ├── file_a2_a.tif
│ ├── file_a2_b.tif
│ ├── subdirectory_a
│ └── subdirectory_b
└── subdir2
├── file_b1_a.tif
├── file_b1_b.tif
├── file_b2_a.tif
├── file_b2_b.tif
├── subdirectory_a
└── subdirectory_b
6 directories, 8 files
And then find regular files at specific level in directory tree and move them. Now we can use -execdir
to achieve exactly what we want
$ find /tmp/root -maxdepth 2 -mindepth 2 -type f -execdir bash -c 'case ${1} in *a.tif) echo cp ${1} ./subdirectory_a ;; *b.tif) echo cp ${1} subdirectory_b ;; esac' sh {} \;
cp ./file_b2_b.tif subdirectory_b
cp ./file_b2_a.tif ./subdirectory_a
cp ./file_b1_a.tif ./subdirectory_a
cp ./file_b1_b.tif subdirectory_b
cp ./file_a1_b.tif subdirectory_b
cp ./file_a1_a.tif ./subdirectory_a
cp ./file_a2_b.tif subdirectory_b
cp ./file_a2_a.tif ./subdirectory_a
Note that in this case, I use echo before moving to ensure everything behaves properly , and no actual moving is done. So if we remove echo
, this will behave correctly:
$ find /tmp/root -maxdepth 2 -mindepth 2 -type f -execdir bash -c 'case ${1} in *a.tif) cp ${1} ./subdirectory_a ;; *b.tif) cp ${1} subdirectory_b ;; esac' sh {} \;
$ tree /tmp/root
/tmp/root
├── subdir1
│ ├── file_a1_a.tif
│ ├── file_a1_b.tif
│ ├── file_a2_a.tif
│ ├── file_a2_b.tif
│ ├── subdirectory_a
│ │ ├── file_a1_a.tif
│ │ └── file_a2_a.tif
│ └── subdirectory_b
│ ├── file_a1_b.tif
│ └── file_a2_b.tif
└── subdir2
├── file_b1_a.tif
├── file_b1_b.tif
├── file_b2_a.tif
├── file_b2_b.tif
├── subdirectory_a
│ ├── file_b1_a.tif
│ └── file_b2_a.tif
└── subdirectory_b
├── file_b1_b.tif
└── file_b2_b.tif
6 directories, 16 files
Note of course that this uses copying to avoid data loss. We can further clean up and in the end - we get the desired directory tree structure.
$ find /tmp/root -maxdepth 2 -mindepth 2 -type f -delete
$ tree
.
├── subdir1
│ ├── subdirectory_a
│ │ ├── file_a1_a.tif
│ │ └── file_a2_a.tif
│ └── subdirectory_b
│ ├── file_a1_b.tif
│ └── file_a2_b.tif
└── subdir2
├── subdirectory_a
│ ├── file_b1_a.tif
│ └── file_b2_a.tif
└── subdirectory_b
├── file_b1_b.tif
└── file_b2_b.tif
6 directories, 8 files
In conclusion what we've done can be expressed as one single script
find /tmp/root -maxdepth 1 -mindepth 1 -exec bash -c 'mkdir ${1}/subdirectory_{a,b}' sh {} \; &&
find /tmp/root -maxdepth 2 -mindepth 2 -type f -execdir bash -c 'case ${1} in *a.tif) cp ${1} ./subdirectory_a ;; *b.tif) cp ${1} subdirectory_b ;; esac' sh {} \; &&
find /tmp/root -maxdepth 2 -mindepth 2 -type f -delete
Of course, steps 2 and 3 can be condensed into one if we use mv
instead of cp
, but I would rather err on the side of caution than to risk data loss.