简体   繁体   中英

Move Files with common naming convention to consolidated folders

Similar to this: Move Movies of different Encodes to a common Folder with original Movie Title

I have a series of PSB and Png export files I wanted all moved to common directories. I don't care if its PowerShell or Bash. I tried both the solutions on that link above, but neither worked. Mine at least appears to be a simpler problem though that I can't find a solution to....

I have several files that I just have the following formatting:

Filename1.psb Filename2.psb filename3.psb Filename4final.gammaOMega.psb Example3.psb Example4.png

They might not all be in the same folder, nor all have common casing and may have random characters or spacing in the file names. The number Zero, a space, or special character might sometimes be used as well in place of a number. However, I can guarantee the first 4-7 chracters of the naming conventions are the same letters on all files. They are all in my Onedrive folder though, just might not be all in the same subfolder.

My goal is to consolidate all of the files to folders to common Folders as follows:

File Folder
Filename1.psb Filename
Filename2.psb Filename
filename3.psb Filename
Filename4final.gammaOMega.psb Filename
Example3.psb Example
Example4.png Example

Because I occasionally slip up and use the same name of file that may be a different revision, rather than overwriting, I prefer to keep both copies instead and rename the later file with.1.psb, .2.psb, etc and so forth. The file can be overwritten if the name, size, and date modified are identical.

What would be the best solution for this in Windows 10? I wouldn't even care if there was a simply batch file solution even.

Would it also be possible to set up a flag to ignore certain directories within the OneDrive folder as well?

DISCLAIMER this answer is based on those statements

If you want to dynamically create those folder when two files got something in common on the name

Yup that's what I'm saying.

If you want to only extract the alphabetical part of the filenames before the first digit to make it a folder name, it's more simpler and you can go for this solution .

But if you want to dynamically generate folder on what two filenames have in common regardless of if this is alphabetical or numerical characters, you could try something like:

#!/bin/bash

# Change those variable according to your wanted path
# You can just export them before starting this script to override those default values
[[ -z $FOLDERS_PATH ]] && FOLDERS_PATH="."
[[ -z $FILES_PATH ]] && FILES_PATH="."

declare -a FOLDER_NAMES

# Convert a string into PascalCase
to_pascal_case() {
  str="${1,,}"
  echo "${str^}"
}
  
# Get a list of directory to create based on what file's names got in common
directories_to_create() {
  find "${FILES_PATH}" -type f|sed -e 'N;s/^\(.*\).*\n\1.*$/\1/'|awk -F '/'  '
   BEGIN {
    previous_file="to_ignore"
   } 
   ($NF ~ ".+") {
     file=tolower($NF); 
     files[file]=file
   } 
   END {
     asorti(files, sorted_files); 
     for (i in sorted_files) {
       file=sorted_files[i]; 
       if(!(file ~ previous_file".*")) {
         print file
       }
       previous_file=file
     }
   }'
}

# Create the directory dynamically and build an array of those directories
create_directories() {
  while read -r folder_name; do
    formatted_folder_name="$(to_pascal_case "${folder_name}")"
    mkdir -p "${FOLDERS_PATH}/${formatted_folder_name}"
    FOLDER_NAMES+=("${formatted_folder_name}")
  done < <(directories_to_create)
}

# Move if the filename match the directory name
move_if_it_match() {
  file_name="${1}"
  folder_name="${2}"
  complete_file_path="${3}"
  [[ ${file_name,,} =~ ^"${folder_name,,}".* ]] && mv "${complete_file_path}" "${FOLDERS_PATH}/${folder_name}"
}

# Moving the files if their name match with the folders (case insensitive match)
move_files() {
  find "${FILES_PATH}" -type f|while read -r file_path; do
    file_name="$(basename "$file_path")"
    for folder_name in "${FOLDER_NAMES[@]}"; do
      move_if_it_match "${file_name}" "${folder_name}" "${file_path}"
    done
  done
}

# Main program
create_directories
move_files

Demo:

$ find . -type f
./Example3.psb
./Example4.png
./Example5.png
./move.sh
./filename3.psb
./Filename1.psb
./Filename2.psb
./Filename4final.gammaOMega.psb
./Example with_spaces.mp4
$ bash move.sh 
$ find . -type f
./move.sh
./Example/Example3.psb
./Example/Example4.png
./Example/Example5.png
./Example/Example with_spaces.mp4
./Filename/filename3.psb
./Filename/Filename1.psb
./Filename/Filename2.psb
./Filename/Filename4final.gammaOMega.psb

Would you please try the following:

#!/bin/bash

while IFS= read -r -d "" f; do
    base=${f##*/}; base=${base//[^a-zA-Z]*}     # extract "basename"
    dir=${base,,}; dir=${dir^}                  # convert to Camelcase
    mkdir -p -- "$dir"                          # create the directory
    mv -- "$f" "$dir"                           # move to the directory
done < <(find . -type f \( -iname "*.psb" -o -iname "*.png" \) -print0)
  • The find.. statement finds the specified files recursively and feed the filenames to the while loop delimited with the null character (to allow whitespaces and other special characters in the filename).
  • The -d "" option tells read to split the input on the null characters.
  • ${f##*/} removes directory path.
  • ${base//[^a-zA-Z]*} removes non-alphabetical character and the following characters.
  • ${base,,} converts the characters to lowercase.
  • ${dir^} converts the first character to uppercase.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM