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:
- Find all the files with their paths;
- Create a
tmp
name and create a sub-directory with that temp name; - Move the found file into the temp directory;
- 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