0

Looking to flatten the directory (with duplicate child file names) while renaming all files, keeping duplicates OR appending all file names with an incrementing number (incrementing # + name would be preferred but not required). This would need to work on MacOS - updated bash to 5.0.11. This is part of a multi-step process (unzipping recursively, flattening while keeping duplicates, pdftotxt, grep for strings > txt file) but this is the part I can't get figured out after a few hours of searching/testing.

Example:

Dir1
Folder A
  - file1.*
  - File2.*
Folder B
  - file1.*
  - File2.*
  - file3.*
Folder C
  - File1.*

Desired output:

Dir1
  - 1-file1.*
  - 2-File2.*
  - 3-file1.*
  - 4-File2.*
  - 5-file3.*
  - 6-File1.*

Any help?

  • No real help... I reviewed most of those before posting. The biggest problem I'm having is appending an incremental number to the file names recursively. 'find /Users/XXX -name *.* -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); echo mv "{}" "$new"' \;' this changes all file names but doesn't increment a number to the name. – jah6719 Jan 12 at 18:22
  • Essentially, I'm looking for the terminal command equivalent of the Finder function "rename (multiple) items" where it gives you the option to rename with format [name+counter]. – jah6719 Jan 12 at 18:30
  • name with format [name+counter]. this isn't what you ask above. – xenoid Jan 12 at 20:03
  • I can flatten a directory structure, the problem is it only keeps one file which are named the same. Is there a quick find or mv command (MacOS) which will append an increment recursively, then my other code could flatten? – jah6719 Jan 13 at 1:04
0

As a two-step process:

Single level

Starting with

.
├── a
│   ├── file1.pdf
│   ├── file1.txt
│   ├── file2.pdf
│   └── file2.txt
└── b
    ├── file1.pdf
    └── file1.txt

1) move files to a directory named after their root name:

├── a
│   ├── file1
│   │   ├── file1.pdf
│   │   └── file1.txt
│   └── file2
│       ├── file2.pdf
│       └── file2.txt
└─ b
    └── file1
        ├── file1.pdf
        └── file1.txt

2) Iterate these sub-directories while incrementing an index and move/rename the files

├── 1-file1.pdf
├── 1-file1.txt
├── 2-file2.pdf
├── 2-file2.txt
├── 3-file1.pdf
└── 3-file1.txt

Code (for bash)

# To be run with your Dir1 as the top directory.

tree . 

for f in */* # iterate files in directories
do
    dir="${f%.*}"  # same with dropped extension
    mkdir -p "$dir"
    mv "$f" "$dir"
done

tree .

i=0
for d in */*/ # iterate subdirectories create above
do
    (( ++i ))
    for f in "$d"/*
    do
        mv "$f" $i-${f##*/} # drop path components and prefix with index
    done
done
rmdir */*/ */
tree .

(you can of course remove the calls to tree).

Multi-level

Starting with:

.
├── a
│   ├── file1.pdf
│   ├── file1.txt
│   ├── file2.pdf
│   └── file2.txt
└── b
    ├── c
    │   ├── file1.pdf
    │   └── file1.txt
    ├── file1.pdf
    └── file1.txt

The intermediate step will give:

.
├── a
│   ├── file1
│   │   ├── file1.pdf
│   │   └── file1.txt
│   └── file2
│       ├── file2.pdf
│       └── file2.txt
└── b
    ├── c
    │   └── file1
    │       ├── file1.pdf
    │       └── file1.txt
    └── file1
        ├── file1.pdf
        └── file1.txt

and final is:

├── 1-file1.pdf
├── 1-file1.txt
├── 2-file2.pdf
├── 2-file2.txt
├── 3-file1.pdf
├── 3-file1.txt
├── 4-file1.pdf
└── 4-file1.txt

Full code:

shopt -s extglob
shopt -s globstar
shopt -s nullglob
tree . 

dirs=( */ ) 
for f in **/*.*
do
    dir="${f%.*}"
    mkdir -p "$dir"
    mv "$f" "$dir"
done

tree .

i=0 
for d in **/
do
    files=("$d"/*.*)
    [[ ${#files[@]} -eq 0 ]] && continue
    (( ++i ))
    for f in "${files[@]}"
    do
        mv -v "$f" $i-${f##*/}
    done
done
rm -r */
tree .

This code makes the assumption that all your files have a dot in their name (extension), while directories don't.

| improve this answer | |
  • This appends a 1 to the file(s) in the top directory, nothing in the children. I did point it to the speciic folder with the undesired side-effect of it renamed everything but moved it to my home folder. – jah6719 Jan 12 at 19:23
  • The script is supposed to run with the top directrory as the CWD. The tree outputs show above are from an actual execution of the script. Your example has only one level of subdirectories, but with little modification the code above can be made to run with several levels. – xenoid Jan 12 at 20:03
  • What would need to be changed to be completely recursive, regardless of how many child folders? – jah6719 Jan 13 at 1:02
  • See edited answer. Check on a copy of your own data, and never trust the code from strangers unless you fully understand it. – xenoid Jan 13 at 9:49

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.