I'm looking to do a linux sh script that

  1. parse a folder to look for a certain extension
  2. copy all these files in a certain folder based of a part of the file name.

Currently I succeeded to create two command lines,

One to create the folders based on a part of the file name, the only missing point is that I would prefer to create all theses folders in the "Out" folder and not in the current folder :

find -maxdepth 1 -type f \( -name "*.xxx" -o -name "*.yyy" \) \
 | tr -s '[:blank:]' '_' \
 | cut -d"-" -f1 \
 | xargs mkdir -p

And the second to copy all the file found in a static folder Out :

find -maxdepth 1 -type f \( -name "*.xxx" -o -name "*.yyy" \) -exec cp '{}' Out \

I do not succeed to merge both command in order to copy files in a subfolder based on its name (subfolder to be created if not existing)

Thanks for help !

  • 1
    A few examples of filenames and where you think they should end up would benefit the question.
    – Kusalananda
    Sep 18, 2019 at 6:29
  • 519_RU-1801013_20181217_141826_656.xxx 519_RU-1801013_20181217_143011_657.xxx AB47 825_RU-1809008_20190723_015635_947.yyy
    – DeFla
    Sep 18, 2019 at 6:41
  • 1
    Could you please give at least 3 filenames and where they should go. Please edit your question with those details.
    – markgraf
    Sep 18, 2019 at 6:43

3 Answers 3


Here, it would be easier with dedicated file renaming tools like prename, mmv or zsh's zmv. But if you have to use POSIX sh and utilities, I'd do things like:

find . ! -name . -prune \( -name '*.xxx' -o -name '*.yyy' \) -type f -exec sh -c '
  for file do
    dstdir=Out/$(printf "%s\n" "${name%%-*}" | tr -s "[:space:]" "[_*]")
    mkdir -p "$dstdir" &&
      cp "$file" "$dstdir/" &&
      true > "$file" || ret=$?
  exit "$ret"' sh {} +

(also avoiding your several GNUisms).

  • Nice touch with $ret. It's usually the first thing I forget about. Can you confirm that the snippet tr -s "[:space:]" "[_*]" is about replacing every space encountered by as many _ signs and then ( -s ) squeezing those underscores down to 1 only ?
    – Cbhihe
    Sep 18, 2019 at 9:23
  • @Cbhihe, yes, it's the standard/POSX version of BSD/GNU's tr -s "[:space:]" _. [:space:] includes more spacing characters than [:blank:] (it's guaranteed to be a superset), but beware that GNU tr doesn't support the multibyte ones (yet) Sep 18, 2019 at 10:06

I'd do that in two passes:

First, create the directories as you did...

find -maxdepth 1 -type f \( -name "*.xxx" -o -name "*.yyy" \) \
| tr -s '[:blank:]' '_' \
| cut -d"-" -f1 \
| xargs mkdir -p

And second making use of bash brace-expansion...

find . -maxdepth 1 -type d -not -name '.' -exec bash -c 'cp ${1}*.{xxx,yyy} ${1}/' _ {} \;


$ tree
├── 519_RU
│   ├── 519_RU-1801013_20181217_141826_656.xxx
│   └── 519_RU-1801013_20181217_143011_657.xxx
├── 519_RU-1801013_20181217_141826_656.xxx
├── 519_RU-1801013_20181217_143011_657.xxx
├── 825_RU
│   └── 825_RU-1809008_20190723_015635_947.yyy
├── 825_RU-1809008_20190723_015635_947.yyy
└── AB47

You could replace in xxx yyy with a command generating a list of extensions.


Thanks for your help, I finaly found the solution, based on another topic that helped me a lot (Here.

Please find it below :

find -maxdepth 1 -type f \( -name "*.xxx" -o -name "*.yyy" \) -size +0 -exec env file={} bash -c 'base="$(basename "$file" | tr -s '[:blank:]' '_' |cut -d"-" -f1)" ;dir="Out/${base%.*}";mkdir -p "$dir";cp "$file" "$dir" ; truncate "$file" -s 0 ' \;
  • 2
    If you could update the question with the requested info, we would be able to give you a better solution. It looks as if you don't actually have to use find at all since you know exactly what files you want to copy and where they are. Your solution also does a lot of other things that you never said anything about in the question (like checking the size of the files and truncating them after copying them).
    – Kusalananda
    Sep 18, 2019 at 7:17

You must log in to answer this question.

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