Home > Back-end >  Create Folder for Each File in Recursive Directory, Placing File in Folder
Create Folder for Each File in Recursive Directory, Placing File in Folder

Time:12-04

Create Folder for Each File in Recursive Directory, Placing File in Folder

On MacOS, so far I have . .

for file in $(ls -R); do if [[ -f "$file" ]]; then mkdir "${file%.*}"; mv "$file" "${file%.*}"; fi; done

This operates correctly on the top level of the nested folder, but does nothing with lower levels.

To isolate the error, I tried this instead, operating on rtf files . .

for i in $(ls -R);do
    if [ $i = '*.rtf' ];then
        echo "I do something with the file $i"
    fi
done

This hangs, so I simplified to . .

for i in $(ls -R); do echo "file is $i" done

That hangs also, so I tried . .

for i in $(ls -R); do echo hello

That hangs also.

ls -R works to provide a recursive list of all files.

Suggestions appreciated !!

CodePudding user response:

First of all don't use ls in scripts. It is meant to show output interactively. Although the newish GNU ls version has some features/options for shell parsing, not sure about on a Mac though.

Now using find with the sh shell.

find . -type f -name '*.rtf' -execdir sh -c '
  for f; do mkdir -p -- "${f%.*}" && mv -v -- "$f" "${f%.*}"; done' _ {}  

For whatever reason -execdir is not available, one can use -exec

find . -type f -name '*.rtf' -exec sh -c '
  for f; do mkdir -p -- "${f%.*}" && mv -v -- "$f" "${f%.*}" ; done' _ {}  

CodePudding user response:

Given this file structure:

$ tree .
.
├── 123
│   └── test_2.rtf
├── bar
│   ├── 456
│   │   └── test_1.rtf
│   └── 789
└── foo

There are two common ways to find all the .rtf files in that tree. The first (and most common) is to use find:

while IFS= read -r path; do 
    echo "$path"
done < <(find . -type f -name "*.rtf")

Prints:

./bar/456/test_1.rtf
./123/test_2.rtf

The second common way is to use a recursive glob. This is not a POSIX way and is only found in more recent shells such as Bash, zsh, etc:

shopt -s globstar            $ In Bash, this enables recursive globs
for path in **/*.rtf; do 
    echo "$path"
done 
# same output

Now that you have the loop to find the files, you can modify the files found.

The first issue you will run across is that you cannot have two files with the same name in a single directory; a directory is just a type of file. So you will need to proceed this way:

  1. Find all the files with their paths;
  2. Create a tmp name and create a sub-directory with that temp name;
  3. Move the found file into the temp directory;
  4. Rename the temp directory to the found file name.

Here is a Bash script to do that:

shopt -s globstar
for p in **/*.rtf; do 
    [ -f "$p" ] || continue    # if not a file, loop onward
    tmp=$(uuidgen)             # easy temp name -- not POSIX however
    fn="${p##*/}"              # strip the file name from the path
    path_to="${p%/*}"          # get the path without the file name
    mkdir "${path_to}${tmp}"   # use temp name for the directory 
    mv "$p" "${path_to}$tmp"   # move the file to that directory
    mv "${path_to}$tmp" "$p"   # rename the directory to the path
done 

And the result:

.
├── 123
│   └── test_2.rtf
│       └── test_2.rtf
├── bar
│   ├── 456
│   │   └── test_1.rtf
│   │       └── test_1.rtf
│   └── 789
└── foo
  • Related