由于文件名中有特殊字符,无法重命名文件



我在USB驱动器上有来自Windows-PC的文件,其文件名中有特殊字符。在macOS上,这是一个问题,因为以下行为:

  • % ls(不含-a)可以看到
  • 文件可以被% find .
  • 看到
  • 文件不能用rm
  • 删除
  • 包含文件夹的所有内容都可以被删除
  • 在Finder.app
  • 中不可见
  • 包含的文件夹不能在Finder中复制,停止并出现错误信息The operation can’t be completed because one or more required items can’t be found. (Error code -43)
  • 不能用% mv重命名,出现No such file or directory
  • 我可以在macOS中创建一个同名的文件
  • 文件在Windows 10上正常工作
  • 在macOS中删除包含的文件夹会显示它们在垃圾桶中,但是垃圾桶不能清空

在Windows上,这些文件可以完美地工作,并且可以重命名,然后它们可以在macOS上工作。

问题是文件名中有包含两个unicode码点的字符。如果我在USB驱动器上的Windows-PC上编辑文件名,我需要删除两次以摆脱ü,因为它由u和¨组成。

的例子:

在使用FAT32或ExFAT格式化的u盘上,用两个unicode码点组合的字符命名文件:U+0075后跟U+0308(Windows)。

安装macOS Ventura操作系统的Macbook
% ls
ü.txt
% mv ü.txt test.txt
mv: rename ü.txt to test.txt: No such file or directory

如何在macOS上重命名文件?

USB驱动器格式化为FAT32,我也尝试过ExFAT,同样的损坏行为。macOS被格式化为APFS

因为我希望将来我得到的一些文件也有这样的文件名,我想避免为了这个唯一的目的而需要一台PC。


Z Shell示例脚本

我试着用hc_dev的答案来创建一个工作脚本。

# Filename is removeUTF16inFilename.zsh
# A script to remove any UTF-16 characters from the file name and adding one ASCII character in case the file name only consists of UTF-16 characters.
# Issue: Windows uses UTF-16 for encoding file-names. But macOS uses UTF-8 in Unicode NFD (Normalization Form Canonical Decomposition).
# Usage: Copy the script in the folder with the problematic files, then run the script with % zsh removeUTF16inFilename.zsh
count=1
for file in *
do
if [[ $file != "removeUTF16inFilename.zsh" ]]
then
echo "File number: $count"
echo "File name: $file"
newFilename=$(echo $file | tr -cd 'A-Za-z0-9_.-')
mv $file $count$newFilename
echo "Renaming $file to $count$newFilename"
((count++))
echo
fi
done

仍然错误No such file or directory.


Swift 5示例脚本

import Foundation
let fileManager = FileManager.default
let currentDirectoryPath = fileManager.currentDirectoryPath
do {
let contentsOfDirectory = try fileManager.contentsOfDirectory(atPath: currentDirectoryPath)

var count = 1
for filename in contentsOfDirectory {
if filename == "removeUTF16inFilename.swift" {
continue
}
print("File number: (count)")
print("Filename: (filename)")
let newFilename =  String(count) + String(filename.unicodeScalars.filter{ $0.isASCII })

print("Renaming file to (newFilename) n")
let filePathOld = currentDirectoryPath + "/" + filename
let filePathNew = currentDirectoryPath + "/" + newFilename
try fileManager.moveItem(atPath: filePathOld, toPath: filePathNew)

count = count + 1
}
} catch {
print(error.localizedDescription)
}
print("Script finished")

:

% pwd
/Volumes/BLACK/test
% ls -l
total 416
-rwx------@ 1 meuser  staff    947 Dec 30 14:15 removeUTF16inFilename.swift
-rwx------  1 meuser  staff  88836 Nov 18  2020 ü.pdf
-rwx------  1 meuser  staff      0 Dec 30 10:46 ü.txt
% swift removeUTF16inFilename.swift 
File number: 1
Filename: ü.txt
Renaming file to 1u.txt 
“ü.txt” couldn’t be moved to “test” because either the former doesn’t exist, or the folder containing the latter doesn’t exist.
Script finished

Hexdump

735 14:51 % pwd
/Volumes/BLACK/test
736 14:51 % ls
ü.txt
737 14:51 % ls -l | hexdump -C
00000000  74 6f 74 61 6c 20 30 0a  2d 72 77 78 2d 2d 2d 2d  |total 0.-rwx----|
00000010  2d 2d 20 20 31 20 6e 69  74 72 6f 20 20 73 74 61  |--  1 nitro  sta|
00000020  66 66 20 20 30 20 44 65  63 20 33 30 20 31 30 3a  |ff  0 Dec 30 10:|
00000030  34 36 20 75 cc 88 2e 74  78 74 0a                 |46 u...txt.|
0000003b
748 14:59 % ls
ü.txt
749 14:59 % ls | hexdump -C
00000000  75 cc 88 2e 74 78 74 0a                           |u...txt.|
00000008
750 14:59 % echo .txt | hexdump -C
00000000  2e 74 78 74 0a                                    |.txt.|
00000005

0a是结尾,所以有问题的ü一定是75 cc 88ü在macOS上是c3 bc.


在macOS中创建的名为ü.txt的文件和在Windows中创建的名为ü.txt的错误文件的Hexdump。

% ls
ü.txt   ü.txt
ls | hexdump -C   
00000000  75 cc 88 2e 74 78 74 0a  75 cc 88 2e 74 78 74 0a  |u...txt.u...txt.|
00000010

printf也不工作

762 15:17 % ls
ü.txt
763 15:18 % ls | hexdump -C
00000000  75 cc 88 2e 74 78 74 0a                           |u...txt.|
00000008
764 15:18 % cp $(printf "x75xccx88").txt uuuuu.txt
cp: ü.txt: No such file or directory

示例脚本

脚本中的第一个命令仅在PowerShell中复制和使用时有效,而在脚本中运行时无效。Visual Studio Code在Windows和macOS上保留Unicode字符。大多数应用程序自动将字符从U+0075后面跟着U+0308转换为U+00FC,例如XcodeTextEdit

从这里下载:workupload

Issue

Windows使用UTF-16用于编码文件名。但是macOS在UnicodeNFD(规范化形式规范分解)中使用UTF-8。

:

  • osx mavericks - Windows与Mac OS X的Unicode文件名-超级用户
  • macos -文件名中不同的utf8编码os x -堆栈溢出
  • 终端-如何剥特殊字符的文件名?-询问不同的

重命名使用tr -cd删除UTF-16字符

根据AskDifferent的回答建议,使用具有有限字符集(例如ASCII)的翻译命令。与mv结合使用,可以删除目标文件名中所有不需要的字符,如UTF-16。

在激活它之前尝试在这个模拟中重命名文件:

for file in *; do echo mv "$file" `echo $file | tr -cd 'A-Za-z0-9_.-'` ; done

注意:要激活重命名,请删除阻止的"echo "在mv命令前面。

使用索引节点编号

像许多Unix/Linux系统一样,BSD也有inode的概念。要显示文件的索引节点,使用-i标志和ls:
ls -i
12382580 _ü.txt

然后可以使用find * -inum 12382580再次选择文件并传递下一个命令,例如使用-exec,其他命令选项或-printxargs的管道(分别为-print0xargs -0)。

参见:

  • 唯一标识文件
  • 文件系统—当用NFS挂载时,我如何在一个目录中有两个同名的文件?-超级用户

最新更新