如何重命名文件名以避免在Windows或Mac中发生冲突?

如何批量重命名文件名,以便它们不包含与其他文件系统冲突的字符,例如,

Screenshot 2015-09-07-25:10:10 

请注意,冒号是此文件名中的问题。 这些不会被Windows或Mac消化。

这些文件可以重命名为

 Screenshot 2015-09-07-25--10--10 

我必须将大量文件从Ubuntu移动到另一个操作系统。 我使用Rsync将它们复制到NTFS驱动器,但丢失了一些文件。 我还将它们复制到ext4驱动器。

以下列表是保留字符:

  (greater than) : (colon) " (double quote) / (forward slash) \ (backslash) | (vertical bar or pipe) ? (question mark) * (asterisk) 

另一个问题是Windows在文件名(以及大多数OS X系统)方面不区分大小写。

你可以这样做:

 rename 's/[<>:"\\|?*]/_/g' /path/to/file 

这将用_替换所有这些字符。 请注意,您不需要替换/ ,因为它是两个文件系统中文件名的无效字符,但用作Unix路径分隔符。 使用以下命令扩展到目录及其所有内容:

 find /path/to/directory -depth -exec rename 's/[<>:"\\|?*]/_/g' {} + 

请注意, / (标记模式的结尾)和\都被转义。 要保留唯一性,您可以为其附加一个随机前缀:

 $ rename -n 's/[<>:"\/\\|?*]/_/g && s/^/int(rand(10000))/e' a\\b a\b renamed as 8714a_b 

更完整的解决方案至少应该:

  1. 将所有字符转换为相同的大小写
  2. 使用理智的计数系统

也就是说, foo.mp3不应该变成foo.mp3.1 ,而应该变成foo.1.mp3 ,因为Windows更依赖于扩展。

考虑到这一点,我写了以下脚本。 我试图通过使用前缀路径来非破坏性,我可以复制重命名的文件,而不是修改原始文件。

 #! /bin/bash windows_chars='<>:"\|?*' prefix="windows/" # Find number of files/directories which has this name as a prefix find_num_files () ( if [[ -e $prefix$1$2 ]] then shopt -s nullglob files=( "$prefix$1-"*"$2" ) echo ${#files[@]} fi ) # From http://www.shell-fu.org/lister.php?id=542 # Joins strings with a separator. Separator not present for # edge case of single string. str_join () ( IFS=${1:?"Missing separator"} shift printf "%s" "$*" ) for i do # convert to lower case, then replace special chars with _ new_name=$(tr "$windows_chars" _ <<<"${i,,}") # if a directory, make it, instead of copying contents if [[ -d $i ]] then mkdir -p "$prefix$new_name" echo mkdir -p "$prefix$new_name" else # get filename without extension name_wo_ext=${new_name%.*} # get extension # The trick is to make sure that, for: # "abc", name_wo_ext is "ab" and ext is ".c" # "abc", name_wo_ext is "abc" and ext is empty # Then, we can join the strings without worrying about the # . before an extension ext=${new_name#$name_wo_ext} count=$(find_num_files "$name_wo_ext" "$ext") name_wo_ext=$(str_join - "$name_wo_ext" $count) cp "$i" "$prefix$name_wo_ext$ext" echo cp "$i" "$prefix$name_wo_ext$ext" fi done 

在行动:

 $ tree a:b a:b ├── b:c │  ├── a:d │  ├── A:D │  ├── a:db │  └── a:Db ├── B:c └── B"c └── a 

该脚本可在我的Github仓库中找到 。

以其他字符串或字符递归替换文件名中的字符串或字符列表

下面的脚本可用于替换每个字符串的任意替换可能出现在文件名中的字符串或字符列表。 由于脚本只重命名文件本身(而不是路径),因此不存在弄乱目录的风险。

替换在list: chars定义(见下文)。 可以为每个字符串提供自己的替换,以便在您想要这样做时能够反转重命名。 (假设替换是一个唯一的字符串)。 如果您想用下划线替换所有有问题的字符串,只需将列表定义为:

 chars = [ ("<", "_"), (">", "_"), (":", "_"), ('"', "_"), ("/", "_"), ("\\", "_"), ("|", "_"), ("?", "_"), ("*", "_"), ] 

受骗者

为防止重复名称,脚本首先创建“新”名称。 然后,它检查同一目录中是否已存在类似命名的文件。 如果是这样,它会创建一个新名称,前面有dupe_1dupe_2 ,直到找到该文件的“可用”新名称:

在此处输入图像描述

变为:

在此处输入图像描述

剧本

 #!/usr/bin/env python3 import os import shutil import sys directory = sys.argv[1] # --- set replacement below in the format ("", "") as below chars = [ ("<", "_"), (">", "_"), (":", "_"), ('"', "_"), ("/", "_"), ("\\", "_"), ("|", "_"), ("?", "_"), ("*", "_"), ] # --- for root, dirs, files in os.walk(directory): for file in files: newfile = file for c in chars: newfile = newfile.replace(c[0], c[1]) if newfile != file: tempname = newfile; n = 0 while os.path.exists(root+"/"+newfile): n = n+1; newfile = "dupe_"+str(n)+"_"+tempname shutil.move(root+"/"+file, root+"/"+newfile) 

如何使用

  1. 将脚本复制到空文件中,将其另存为rename_chars.py
  2. 如果您想要替换列表,请进行编辑。 实际上,scrip0t用下划线替换所有出现问题的字符,但选择权在你手中。
  3. 测试 – 通过命令在目录上运行它:

     python3 /path/to/rename_chars.py  

注意

注意在行中:

 ("\\", "_bsl_"), 

在python中,反斜杠需要通过另一个反斜杠进行转义。