Shell-脚本内部编码不同于重定向输出

Shell-script-internal encoding differs from redirected output

目前,我正在处理一堆旧文件,这些文件在其生命周期中经历过​​很多机器、操作系统和文件系统。其中一些包含德语元音变音符号(ä、ö、ü),显然这些已经导致一些文件名在移动过程中中断。一个原名为

的文件
München.txt

显示为

M?nchen.txt (invalid encoding)

在 ubuntu 系统上,它们当前所在的位置。

所以现在我正在尝试批量修复它们。在使用初稿循环浏览文件时,我偶然发现了这种现象:

这是代码:

#!/bin/bash

rootdir=

find "$rootdir" -print0 | while IFS= read -r -d '' broken_file_name; do
    echo $broken_file_name
done

我正在努力理解:

  1. 为什么屏幕输出与文件中的不同?字符替换发生在哪里,问号对象又在哪里创建?
  2. 如何防止在脚本进程中解释带有问号的非法字符?它阻止我有选择地用相应的正确字符替换非法字符。

less 的不同行为可能是 less 的事情。来自手册:

Control and binary characters are displayed in standout (reverse video). Each such character is displayed in caret notation if possible (e.g. ^A for control-A). Caret notation is used only if inverting the 0100 bit results in a normal printable character. Otherwise, the character is displayed as a hex number in angle brackets. This format can be changed by setting the LESSBINFMT environment variable.

但是由于您想要重命名文件,因此各种实用程序显示名称的方式并不那么重要。在您的脚本中,您可以使用 tr 来计算新名称,方法是将您不喜欢的字符替换为其他字符。例如,如果您想分别用 o 和 u 替换 ö 和 ü:

new=$(tr '64' 'ou' <<< "$old")
if [ "$new" != "$old" ]; then
  mv "$old" "$new"
fi

(366和374是ö和ü的ascii码,八进制)

问号替换可能发生在 Bash 本身,只要您使用 Bash echo 并尝试输出当前语言环境无法表示的字符。它也可能是终端驱动程序的一个特性。

我们只能推测原始编码,但症状与 Latin-1 (ISO-8859-1) 一致。

假设我猜对了编码,并且假设您当前的语言环境是 UTF-8,请尝试类似

while IFS= read -r original; do
    dest=$(iconv -f iso-8859-1 <<<"$original")
    mv -- "$original" "$dest"
done <file_list