I have multiple folders that represent a participant, and each of them have a file called con_0001.img. I would like to move all those files (con_0001.img) to one new folder (rather than having them in individual participant folders).

The code below seems to find con_0001.img and moves it to a new folder, called SA_A:

find . -name "con_0001.img" -exec mv '{}' "./SA_A/" ";"

However, as all the files are named con_0001.img, I end up with just the one file in my new folder (presumably because all subsequent files overwrite each other).

I did manage to find a suggestion for preventing this overwriting, namely:

find . -name "con_0001.img" -exec mv --backup=numbered "{}" "./SA_A/" \;

However, I still end up with the one file and con_0001.img has disappeared from multiple folders, so it seems to be overwriting. If I change mv to cp, I also end up with the one file (but the files stay where they are, as you would expect for copying files).

Is there a way of avoiding this overwriting? And more ideally, is there a way of adding part of the name of its parent directory (participant number) to the file when it is moved to the new folder? For example:

p102/con_0001.img ----> p102_con_0001.img

p105/con_0001.img ----> p105_con_0001.img

I tried the adapt the following code, as it seems that it should add the parent directory to the moved file:

find . -name 'con_0001.img' | while read filePath; do cp "$filePath" './SA_A'/"$(echo "$filePath")"; done

However, it returns an error:

cannot create regular file '/SA_A/./p105/con_0001.img': no such file or directory

There appears to be an unexpected full stop after my new folder /SA_A/. which probably is the reason why the code does not work? Where is this full stop is coming from?

1 Answer 1

mkdir SA_A
find . -name "con_0001.img" -exec sh -c '
    for f; do d=${f%/*}; mv -- "$f" "./SA_A/${d#./}${f##*/}"; done
' findsh {} +

The for loop loops over each found file.

  • d=${f%/*} removes the rightmost /* part of f, i.e., the basename of the file.
  • ${d#./} removes the leading ./ of the file.
  • ${f##*/} gets the basename of the file, removing everything before the last /.

Before execution:

$ ls a1 a2 a3



After execution:

$ ls a1 a2 a3 SA_A/



a1con_0001.img  a2con_0001.img  a3con_0001.img

But it is even simpler with a single shell loop:

for f in */con_0001.img; do
    mv -- "$f" "./SA_A/${f%/*}_${f#*/}"

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.