【问题标题】:Shell-script-internal encoding differs from redirected outputShell-script-internal encoding 与重定向输出不同
【发布时间】:2022-01-06 04:06:24
【问题描述】:

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

的文件
München.txt

显示为

M?nchen.txt (invalid encoding)

在当前托管它们的 ubuntu 系统上。

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

  • 在屏幕上回显给我带有问号的文件名,我理解这是对文件名中非法字符的解释:

     ./list_files.sh path_to_files
    
     M?nchen.txt
     K?ln.txt
    
  • 如果我将输出保存到文件中,它会给我一个仍然包含无效字符的二进制文件:

     ./list_files.sh path_to_files > file_list
    
     less file_list
     M<FC>nchen.txt
     K<F6>ln.txt
    

这是代码:

#!/bin/bash

rootdir=$1

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

我正在努力理解:

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

【问题讨论】:

  • less 的不同行为可能是less 的事情。来自手册:“否则,字符在尖括号中显示为十六进制数字。可以通过设置 LESSBINFMT 环境变量来更改此格式。”(Otherwise 是对于不是控制字符的二进制字符)。而echoprintfcat... 不可打印的字符显示为?
  • ./list_files.sh path_to_files | cat 的问号还在吗?如果是这样,它们可能会被终端打印为无法识别的字节。如果 not,我猜想 iconv 正在被使用,但前提是 stdout 是终端(它打印 ? 以表示无法识别)。字节 \xFC\xF6 不是有效的 ASCII 或 UTF-8,因为它们在 128-255 之间(以 10 为基数)。 less&lt;F6&gt; 一样打印二进制字节 \xF6。您可以使用hexdump(或hd)以获得更清晰的视图。另请查看iconv 及其//TRANSLIT 标志,以帮助转换。

标签: bash ubuntu character-encoding


【解决方案1】:

问号替换可能发生在 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

【讨论】:

  • 如果 Latin-1 不是一个正确的猜测并且您有更多的样本可以推断,也许可以在 tripleee.github.io/8bit 查找它们
  • 您认为iconv//TRANSLIT 标志对这个字符集有帮助吗?
  • @dan 我不明白你为什么想要那个。目标字符集(大概)是 UTF-8,因此原始字符集中应该没有无法表示的字符。
  • 好酷。我在想可能有一些不起眼的字符没有等价物,但我猜变音符号被覆盖了。
  • 不仅如此; Latin-1 有一个奇怪的特性,它的代码点在 8 位范围内与 Unicode 相同。
【解决方案2】:

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

控制和二进制字符以突出显示(反向视频)。如果可能,每个这样的字符都以插入符号显示(例如,^A 表示 control-A)。仅当反转 0100 位导致正常的可打印字符时,才使用插入符号。否则,字符将显示为尖括号中的十六进制数字。可以通过设置 LESSBINFMT 环境变量来更改此格式。

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

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

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

【讨论】:

  • 这将删除重音而不是实际尝试修复它们。
  • 是的。我认为这是OP想要的。我错了吗?
  • 我会假设他们想要回原来的角色。
  • 哦,366 和 374 根本不是 ASCII 码,ASCII 的范围以八进制 177 结尾。您拥有的字符码在 Latin-1 和其他一些 8 位编码中有效。
  • 哦,我知道了,谢谢!这是一个字符编码问题。让我们等到OP确认,我将删除我的答案。建议:为了完整起见,您可以复制粘贴我的解释为什么less 在您自己的答案中表现不同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-26
  • 1970-01-01
  • 2016-03-10
  • 1970-01-01
  • 2019-02-13
  • 2012-02-23
  • 2010-09-11
相关资源
最近更新 更多