Shell-脚本内部编码不同于重定向输出
Shell-script-internal encoding differs from redirected output
目前,我正在处理一堆旧文件,这些文件在其生命周期中经历过很多机器、操作系统和文件系统。其中一些包含德语元音变音符号(ä、ö、ü),显然这些已经导致一些文件名在移动过程中中断。一个原名为
的文件
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=
find "$rootdir" -print0 | while IFS= read -r -d '' broken_file_name; do
echo $broken_file_name
done
我正在努力理解:
- 为什么屏幕输出与文件中的不同?字符替换发生在哪里,问号对象又在哪里创建?
- 如何防止在脚本进程中解释带有问号的非法字符?它阻止我有选择地用相应的正确字符替换非法字符。
与 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
目前,我正在处理一堆旧文件,这些文件在其生命周期中经历过很多机器、操作系统和文件系统。其中一些包含德语元音变音符号(ä、ö、ü),显然这些已经导致一些文件名在移动过程中中断。一个原名为
的文件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=
find "$rootdir" -print0 | while IFS= read -r -d '' broken_file_name; do
echo $broken_file_name
done
我正在努力理解:
- 为什么屏幕输出与文件中的不同?字符替换发生在哪里,问号对象又在哪里创建?
- 如何防止在脚本进程中解释带有问号的非法字符?它阻止我有选择地用相应的正确字符替换非法字符。
与 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