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#*/}"

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .