【问题标题】:Move Files with common naming convention to consolidated folders将具有通用命名约定的文件移动到合并文件夹
【发布时间】:2020-12-30 17:28:30
【问题描述】:

与此类似:Move Movies of different Encodes to a common Folder with original Movie Title

我有一系列 PSB 和 Png 导出文件,我希望将它们全部移到公共目录中。我不在乎它是 PowerShell 还是 Bash。我在上面那个链接上尝试了两种解决方案,但都没有奏效。我的问题至少看起来是一个更简单的问题,尽管我找不到解决方案....

我有几个文件格式如下:

文件名1.psb 文件名2.psb 文件名3.psb 文件名4final.gammaOMega.psb Example3.psb Example4.png

它们可能不都在同一个文件夹中,也不都有相同的大小写,并且文件名中可能包含随机字符或空格。有时也可以使用数字零、空格或特殊字符来代替数字。但是,我可以保证命名约定的前 4-7 个字符在所有文件中都是相同的字母。它们都在我的 Onedrive 文件夹中,只是可能不都在同一个子文件夹中。

我的目标是将文件夹中的所有文件合并到公共文件夹中,如下所示:

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

因为我偶尔会出错并使用可能是不同版本的相同文件名,而不是覆盖,所以我更喜欢保留两个副本并将后面的文件重命名为 .1.psb、.2.psb 等等等。如果名称、大小和修改日期相同,则可以覆盖该文件。

在 Windows 10 中,最好的解决方案是什么?我什至不在乎是否有一个简单的批处理文件解决方案。

是否也可以设置一个标志来忽略 OneDrive 文件夹中的某些目录?

【问题讨论】:

  • 如果FilenameX1.png 存在,它会去哪里?
  • 在该示例中,它将进入“FilenameX”文件夹。
  • 在这种情况下,@NathanGagne @tshiono 的答案可以正常工作。我已经更新了我的答案,以使其回答的陈述更加清晰。例如,当我们昨天说“如果你想在两个文件的名称有共同点时动态创建这些文件夹”“是的,这就是我要说的。” => FilenameX 将移动到 Filename 目录。

标签: bash powershell file directory organization


【解决方案1】:

请您尝试以下方法:

#!/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)
  • find .. 语句递归查找指定的文件并将 while 循环的文件名以空字符分隔(允许 文件名中的空格和其他特殊字符)。
  • -d "" 选项告诉read 将输入拆分为空字符。
  • ${f##*/} 删除目录路径。
  • ${base//[^a-zA-Z]*} 删除非字母字符和以下字符 字符。
  • ${base,,} 将字符转换为小写。
  • ${dir^} 将第一个字符转换为大写。

【讨论】:

    【解决方案2】:

    免责声明此答案基于这些陈述

    如果您想在两个文件名称相同时动态创建这些文件夹

    是的,这就是我要说的。

    如果您只想提取第一个数字之前的文件名的字母部分以使其成为文件夹名称,则更简单,您可以使用this solution

    但是,如果您想动态生成两个文件名共有的文件夹,无论这是字母还是数字字符,您可以尝试以下操作:

    #!/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
    

    演示:

    $ 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
    

    【讨论】:

    • 当我运行它时,它只会生成两个名为“FileName”和“Example”的文件夹我很抱歉,这些文件实际上并没有命名,这些只是我使用的示例,名称文件可以是任何东西。唯一的共同点是它们要么是 psb 文件,要么是 png 文件,并且公共文件都至少在文件名的开头有相同的 4-7 个字符。有没有办法让这些文件夹名称本身成为文件名的上下文变量?另外,我可以让它运行的唯一方法是在最后添加另一个“完成”。它还会出现以下错误:
    • basename:额外的操作数“Photoshop”尝试“basename --help”以获取更多信息。
    • @NathanGagne 您只需将列表folder_names 更改为您想要的任何文件夹。如果您想在两个文件的名称有共同点时动态创建这些文件夹,那确实是另外一回事。在这种情况下,您将不得不更多地解释您想要什么(使用更具体的规则,例如共同的 char 的最小值、优先级规则等)。
    • @NathanGagne 对于您的basename 错误,我猜这是因为您的某些文件的名称中包含空格。所以我已经更新了我的答案来处理这个案例。
    • 是的,这就是我要说的。根据文件名动态生成文件夹,放入同名文件夹。移动具有类似命名约定的其他文件。每个文件名的 4-7 个前导字符相同,后面的任何内容都可能不同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-02
    • 1970-01-01
    • 2013-08-18
    • 1970-01-01
    相关资源
    最近更新 更多