2

I have some files whose name looks like this:

XXXXX_S1_X_XX_X.txt
XXXXX_S2_X_XX_X.txt
XXXXX_S3_X_XX_XXX.txt
S4_X_XX_X.txt
XXXXX_S5_XX_X.txt   
...

I created a list of folders named: S1, S2, S3, .... I would like to move the file XXXXX_S1_X_XX_X.txt in the folder S1, the file XXXXX_S2_X_XX_X.txt in the folder S2 and so on. I wrote this simple loop but I don't know how to copy/move files according to the pattern matching S* in the corresponding folder:

for i in My_list_of_folders.txt
do 
  dir=${i%.txt}
  mkdir "$dir"
  cp "$i" "$dir"
done
3

A slightly modified loop:

for pattern in S1 S2 S3 S4; do
   mkdir -p ./"$pattern"
   for filename in ./*"$pattern"*; do
       [ ! -f "$filename" ] && continue
       mv -i "$filename" "$pattern"/
   done
done

This loops through the pattern strings S1, S2, S3 and S4. The inner loop uses the current pattern string to look for names in the current directory that contains the string anywhere in it. It skips non-regular files (like the directories S1, S2 etc. themselves) and moves everything else that matches to the appropriate directory for that pattern string.

As slm points out in comments (now deleted), the operations in the inner loop may in this case well be shortened into just

[ ! -f "$filename" ] || mv -i "$filename" "$pattern"/

or

[ -f "$filename" ] && mv -i "$filename" "$pattern"/

If you have overlapping patterns, such as S1 and S11, then you will need to do the longer patterns first.

  • I like it very much but it does not work with S11, S12,....*S1* are taken together. – Elb Jul 15 '18 at 17:28
  • 1
    @Elb No it does not, but that wasn't part of the question. You would have to do longer patterns first, e.g. S11 before S1. – Kusalananda Jul 15 '18 at 17:47
  • Oh yes! You're right. To avoid typing "S*".... many times I replaced the first line with: for pattern in S{1..17};....probably this caused the mv, only in the S1 folder, of all the "S1*" items. – Elb Jul 15 '18 at 18:51
  • @Elb S{17..1} would work. – Kusalananda Jul 15 '18 at 19:54
2

As an alternative:

for i in S{4..1};do 
  mkdir -p $i
  while read line; do 
    [ -f "$line" ] && mv "$line" $i
  done < My_list_of_folders.txt
done

How it works:

  • S{4..1} - uses shell expansion to construct the names of dirs
  • mkdir -p $i - forces creation of directory
  • while read ... < My_list_of_folders.txt - loops through lines in file
  • [ -f "$line" ]... - moves each file to corresponding S dir

NOTE: You can adjust the glob ({4..1}) to whatever range suites your use case. To deal with anything larger, we're ordering things in reverse to deal with any overlap S20 .... S1, hence the S{20..1} type of definition of our glob.

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.