0

I have a problem where I need to rename just certain files after the parent folder and afterwards move these to a central folder. Is there a way to do this? I would like to run this on a Synology NAS.

Root
 |-Subf1
 |  |-File.txt
 |  |-File.doc
 |  |-Subf1subf1
 |  |  |-File.xml
 |  |  |-File.xls
 |  |-Subf1subf2
 |  |  |-File.pptx
 |  |  |-File.docx
 |
 |-Subf2
 |  |-File.txt
 |  |-File.doc
 |  |-Subf2subf1
 |  |  |-File.xml
 |  |  |-File.xls

Result should be:

Root
 |-Subf1
 |  |-Subf1.txt
 |  |-Subf1.doc
 |  |-Subf1.xml
 |  |-Subf1.xls
 |  |-Subf1.pptx
 |  |-Subf1.docx
 |
 |-Subf2
 |  |-Subf2.txt
 |  |-Subf2.doc
 |  |-Subf2.xml
 |  |-Subf2.xls

There is no problem with overwriting files as they all have different extensions.

2
  • Do all the file names start with File?
    – roaima
    Dec 1, 2020 at 17:36
  • No, they start different
    – Lorand G
    Dec 1, 2020 at 20:33

2 Answers 2

0
#! /bin/bash

shopt -s globstar  #enabled for '**' to match all files &directories recursively
#shopt -s dotglob  #uncomment to enable to match on hidden files/directories too

cd /path/to/directory/Root
for pathname in ./**/*; do
    [[ -f "$pathname" ]] && echo mv -v -- "$pathname" "${pathname%%/*}/${pathname%%/*}.${pathname##*.}";
done

##then remove remained empty directories
for pathname in ./**/*; do
    [[ -d "$pathname" && -z "$(ls -A -- "$pathname")" ]] && rm -r -- "$pathname";
done
  • [[ -f "$pathname" ]] checks if the $pathname is a file
  • ${pathname%%/*}: Using shell-parameter-expansion, cut the longest suffix from the pathname parameter. cuts everything up-to first slash / character.
  • ${pathname##*.}": same, but this cuts the longest prefix from the pathname parameter; cuts everything up-to last dot . character.
  • [[ -d "$pathname" ]] checks if the $pathname is a directory
  • ... && -z "$(ls -A -- "$pathname")" then checks if basename of the pathname which was a directory, is empty or not.

remove echo when you were happy with the result.

0
0

POSIXly:

cd Root &&
  LC_ALL=C find . \
    -name '.?*' -prune -o \
      -path './*/*/*' \
      -prune \
      -name '*.*' \
      -type f \
      -exec sh -c '
        ret=0
        for file do
          ext=${file##*.}
          sub=${file%/*/*}
          subname=${sub##*/}
          echo mv -i "$file" "$sub/$subname.$ext" || ret=$?
        done
        exit "$ret"' {} +

(remove echo if happy)

Empty dirs can be removed afterwards with:

find . -depth -type d -exec rmdir {} + 2> /dev/null

Or if your find supports the non-standard -empty and -delete:

find . -type d -empty -delete

(-delete implies -depth).

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.