How to sort files into directories and subdirectories by name?
I'm new to ubuntu and also to the programming world, and I don't really know how to do this!
I have a folder with lots of files named as follows:
000_S_001_mpc_asd.json
000_S_001_mpc_asd.nii
000_S_001_mpc_asd_aa.nii
011_S_001_mpc_asd.json
011_S_001_mpc_asd.nii
011_S_001_mpc_asd_aa.nii
000_S_002_mpc_asd.json
000_S_002_mpc_asd.nii
000_S_002_mpc_asd_aa.nii
000_S_001_dtd_rty.bval
000_S_001_dtd_rty.bvec
000_S_001_dtd_rty.nii
000_S_001_dtd_rty.json
011_S_001_dtd_rty.bval
011_S_001_dtd_rty.bvec
011_S_001_dtd_rty.nii
011_S_001_dtd_rty.json
000_S_002_dtd_rty.bval
000_S_002_dtd_rty.bvec
000_S_002_dtd_rty.nii
000_S_002_dtd_rty.json
011_S_001_flf_lkj.json
011_S_001_flf_lkj.nii
011_S_001_flf_lkj_aa.nii
000_S_001_flf_lkj.json
000_S_001_flf_lkj.nii
000_S_001_flf_lkj_aa.nii
000_S_002_flf_lkj.nii
000_S_002_flf_lkj_aa.nii
Let's say xxx_S_xxx is the principal name, and the rest of the file's name gives secondary information (let's call it a secondary name).
I would like to find a specific name into the secondary name and make a folder with this name (for example mpc, dtd or flf), then make subfolders named as the principal name of each file, and into those folders put the respective files. Probably an image will explain better what I'm trying to say.
So for example, the output for the names I gave you above would look like this:
Desired output:
Is this possible to do from the terminal? I would appreciate your help.
My OS is Ubuntu 20.04 LTS
Solution 1:
In cases where the target directory structure does not exist, you can chop the path into pieces and resemble them in the desired order.
#!/bin/bash
while IFS=_ read -r a b c d e; do
mkdir -p target/$d/${a##*/}_${b}_${c}; mv -t $_ ${a}_${b}_${c}_${d}_$e
done < <(printf %s\\n files/*)
Or a regular expression, where the array BASH_REMATCH
records the matched parts. The first array member contains the part that matches the whole regular expression. Substrings matched by parenthesized subexpressions are assigned to the following members.
#!/bin/bash
for i in files/*; do
if [[ $i =~ ^files/([0-9]+_S_[0-9]+)_(mpc|dtd|flf) ]]; then
mkdir -p target/${BASH_REMATCH[2]}/${BASH_REMATCH[1]}; mv -t $_ $i
fi
done
You can also divide the process into two steps where you start by creating the directory structure first, awk
in combination with xargs
optimizes the use of mkdir
and then use, e.g., mmv
for the renaming.
#!/bin/bash
builtin cd files
printf %s\\0 * | \
awk -F _ ' \
BEGIN{ RS = ORS = "\0" } { printf("../target/%s/%s_%s_%s\0", $4, $1, $2, $3) } \
' | xargs -0 mkdir -p
# Remember we have change our working directory.
mmv -m '*_S_*_*_*' '../target/#3/#1_S_#2/#1_S_#2_#3_#4'