使用特殊字符批量重命名(或正确显示)文件

使用特殊字符批量重命名(或正确显示)文件

我有一堆目录和子目录,其中包含带有特殊字符的文件,例如以下文件:

robbie@phil:~$ ls test�sktest.txt 
test?sktest.txt

Find 揭示了转义序列:

robbie@phil:~$ find test�sktest.txt -ls 
424512 4000 -rwxr--r-x   1 robbie   robbie    4091743 Jan 26 00:34 test\323sktest.txt

我什至可以在控制台上输入他们的名字的唯一原因是制表符完成。这也意味着我可以手动重命名它们(并删除特殊字符)。

我已将 LC_ALL 设置为 UTF-8,这似乎没有帮助(在新 shell 上也没有帮助):

robbie@phil:~$ echo $LC_ALL
en_US.UTF-8

我正在使用 ssh 从我的 mac 连接到机器。这是 Ubuntu 安装:

robbie@phil:~$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=7.10
DISTRIB_CODENAME=gutsy
DISTRIB_DESCRIPTION="Ubuntu 7.10"

Shell 是 Bash,TERM 设置为 xterm-color。

这些文件已经存在很长一段时间了,而且它们并不是使用 Ubuntu 安装创建的。所以我不知道系统编码设置曾经是什么。

我已经尝试过以下方法:

find . -type f -ls | sed 's/[^a-zA-Z0-9]//g'

但我找不到一个可以完成我想要的一切的解决方案:

  1. 识别所有具有无法显示字符的文件(上面忽略了太多)
  2. 对于目录树中的所有文件(递归地),执行 mv oldname newname
  3. (可选)将特殊字符(例如 ä)音译为 a(不是必需的,但会很棒)

或者

  1. 正确显示所有这些文件(并且尝试打开它们时应用程序中没有错误)

我有一些零碎的东西,比如迭代所有文件并移动它们,但识别文件并为 mv 命令正确格式化它们似乎是困难的部分。

也欢迎任何有关为什么它们无法正确显示或如何“猜测”正确编码的额外信息。 (我尝试过 convmv 但它似乎并没有完全达到我想要的效果:http://j3e.de/linux/convmv/

答案1

我猜您看到这个无效字符是因为该名称包含无效的 UTF-8 字节序列。典型的 unix 文件系统(包括您的文件系统)上的文件名是字节字符串,由应用程序决定使用什么编码。如今,使用 UTF-8 已成为一种趋势,但它并不通用,尤其是在无法使用纯 ASCII 的语言环境中,并且在 UTF-8 存在之前就一直在使用其他编码。

尝试LC_CTYPE=en_US.iso88591 ls查看文件名在 ISO-8859-1 (latin-1) 中是否有意义。如果没有,请尝试其他区域设置。请注意,这里只有LC_CTYPE区域设置很重要。

在 UTF-8 语言环境中,以下命令将显示名称不是有效 UTF-8 的所有文件:

grep-invalid-utf8 () {
  perl -l -ne '/^([\000-\177]|[\300-\337][\200-\277]|[\340-\357][\200-\277]{2}|[\360-\367][\200-\277]{3}|[\370-\373][\200-\277]{4}|[\374-\375][\200-\277]{5})*$/ or print'
}
find | grep-invalid-utf8

您可以检查它们在其他语言环境中是否更有意义重新编码或者图标:

find | grep-invalid-utf8 | recode latin1..utf8
find | grep-invalid-utf8 | iconv -f latin1 -t utf8

一旦确定一堆文件名采用某种编码(例如 latin1),重命名它们的一种方法是

find | grep-invalid-utf8 |
rename 'BEGIN {binmode STDIN, ":encoding(latin1)"; use Encode;}
        $_=encode("utf8", $_)'

这使用了 perl改名命令在 Debian 和 Ubuntu 上可用。您可以传递它-n来显示它将执行的操作,而无需实际重命名文件。

答案2

我知道这是一个老问题,但我整晚都在寻找类似的解决方案。我发现了一些有用的提示,但它们并没有完全满足我的需要,因此我必须混合搭配一些提示才能获得我正在寻找的正确结果

只需删除特殊字符并将其替换为 (.) 点

for f in *.txt; do mv "$f" `echo $f | sed "s/[^a-zA-Z0-9.]/./g"`; done

为了在 cronjob 中使用,我做了以下每分钟运行一次的操作

*/1 * * * * cd /path/to/files/ && for f in *.txt; do mv "$f" `echo $f | sed "s/[^a-zA-Z0-9.]/./g"`; done >/dev/null 2>&1

我希望有人觉得这很有帮助,因为它让我很开心:)

答案3

现在,当您知道远程端的文件名使用哪种编码(“latin1”——根据第一个答案的注释)时,您还可以按照第二种方式-- 运行本地终端并SSH以这样的方式远程文件名正确显示(而不是第一种方式:重命名它们)

喜欢,您可以在本地启动一个以该特殊编码工作的终端,也许像这样:

LC_ALL=en_US.latin1 xvt &

xvt代表你的终端程序。

也许,现有的语言环境被称为en_US.iso88591,而不是en_US.latin1我假设的那样。

答案4

这不满足批量要求,但我刚刚遇到了类似的问题,我有一个文件的多个版本,其名称相似,仅由一个奇怪的字符不同。不幸的是,这意味着我无法使用我通常使用的通配符技巧来重命名罪犯。

最后,我使用 Filezilla 作为 SFTP 客户端进行连接,浏览文件并使用 GUI 重命名它们。 Filezilla 很好地处理了狡猾的字符。

相关内容