文件名的字符编码问题 - 查找损坏的文件名

文件名的字符编码问题 - 查找损坏的文件名

我遇到了此问答。可能是因为 Linux 发行版比较老旧,或者 Windows 中有几个文件名损坏。ls显示“?”而不是损坏的字符。我成功重命名了其中一些文件,但我不知道是否找到了所有文件。

有什么方法可以找到所有受影响的文件吗?

答案1

假设您使用的是 utf-8 编码(Ubuntu 中的默认编码),该脚本应该可以识别文件名并为您重命名。

它的工作原理是使用 find 和 C 编码 (ASCII) 来查找包含不可打印字符的文件。然后,它会尝试确定这些不可打印字符是否为 utf-8 字符。如果不是,它会向您显示使用数组中列出的每种编码解码的文件名enc,让您选择看起来正确的文件名以对其进行重命名。

latin1 通常用于较旧的 Linux 系统,而 windows-1252 是当今 Windows 中常用的(我认为)。iconv -l将显示可能的编码列表。

#!/bin/bash

# List of encodings to try. (max 10)
enc=( latin1 windows-1252 )

while IFS= read -rd '' file <&3; do
    base=${file##*/} dir=${file%/*}

    # if converting from utf8 to utf8 succeeds, we'll assume the filename is ok.
    iconv -f utf8 <<< "$base" >/dev/null 2>&1 && continue

    # display the filename converted from each enc to utf8
    printf 'In %s:\n' "$dir/"
    for i in "${!enc[@]}"; do
        name=$(iconv -f "${enc[i]}" <<< "$base")
        printf '%2d - %-12s: %s\n' "$i" "${enc[i]}" "$name"
    done
    printf ' s - Skip\n'

    while true; do
        read -p "? " -n1 ans
        printf '\n'
        if [[ $ans = [0-9] && ${enc[ans]} ]]; then
            name=$(iconv -f "${enc[ans]}" <<< "$base")
            mv -iv "$file" "$dir/$name"
            break
        elif [[ $ans = [Ss] ]]; then
            break
        fi
    done
done 3< <(LC_ALL=C find . -depth -name "*[![:print:][:space:]]*" -print0)

答案2

尝试这个:

find / | grep -P "[\x80-\xFF]"

这将查找文件和文件夹名称中的所有非 ASCII 字符,并帮助您找到罪魁祸首 :P

答案3

从这个正则表达式查找命令开始并修改它直到你只找到你感兴趣的内容:find . | egrep [^a-zA-Z0-9_./-\s]
上面的命令将找到包含非 UTF-8 字符的文件名。

答案4

这是另一个版本,非常简单,它使用unidecode实用程序 (install: sudo apt install python3-unidecode),它会尽力将 Unicode 字符转换为“等效” ASCII 字符。显然,这会带来一些警告;请参阅https://pypi.org/project/Unidecode/

它首先重命名目录,然后重命名文件(同时更改目录名称和文件可能会产生不可预测的结果,重命名目录时必须进行深度优先。)

(通过一些小的改动,您可以只打印出需要更改的内容,而不是进行实际的更改。)

#!/bin/bash

[ ! -d "$1" ] && echo "** error: expecting directory to search" && exit 2
DIR=$1

do_rename() {
  orig=$1
  new=$(unidecode -c "$1")

  printf "mv -i \"%s\" \"%s\"\n" "$orig" "$new" ## DEBUG
  mv -i "$orig" "$new"
}
export -f do_rename

# directories
LC_ALL=C find $DIR -depth -type d -name '*[! -~]*' -exec bash -c 'do_rename "$0"' {} \;

# files
LC_ALL=C find $DIR -depth -type f -name '*[! -~]*' -exec bash -c 'do_rename "$0"' {} \;

相关内容