如何在bash中反转转义反斜杠编码,例如"\ "和"\303\266"?

5

我有一个记录UTF8编码文件名的脚本。然而,脚本的编码/环境没有设置正确,它只是重新编码了原始字节。现在我的文件中有很多这样的行:

.../My\ Folders/My\ r\303\266m/...

所以,在文件名中存在带有\和UTF8编码的内容,例如\303\266(表示ö)。我想要反转这种编码?是否有一些简单的bash命令行命令可以链在一起使用以删除它们?
我可以得到数百万个sed命令,但是列出所有非ASCII字符将需要很长时间。或者开始在Python中解析它。但我希望有一些诀窍可以使用。
6个回答

6
这是Unicode字符的初步尝试:
text="/My\ Folders/My\ r\303\266m/"
text="echo \$\'"$(echo "$text"|sed -e 's|\\|\\\\|g')"\'"
# the argument to the echo must not be quoted or escaped-quoted in the next step
text=$(eval "echo $(eval "$text")")
read text < <(echo "$text")
echo "$text"

这里使用了Bash的$'string'引用特性。

这会输出"/My Folders/My röm/"。

从Bash 4.4开始,只需要:

text="/My Folders/My r\303\266m/"
echo "${text@E}"

这里使用了 Bash 的一个新特性,称为 参数转换E 操作符使参数被视为其内容位于 $'string' 中,其中反斜杠转义序列(在此情况下为八进制值)被计算。


@Rory:你为什么认为他们称它为“bash”? - Dennis Williamson
谢谢。我在第二行中添加了额外的引号:text="echo \$\'"$(echo \"$text\"|sed -e 's|\\|\\\\|g')"\'",因为存在内部转义空格的问题(是的,不是编码的空格)。 - knalli
@knalli:这些添加的引号不需要转义。但无论哪种方式都可以工作。我会将它们与其他一些内容添加到我的答案中。谢谢。 - Dennis Williamson

2

目前还不清楚使用的是哪种转义方式。八进制字符代码属于C语言,但C语言不会对空格进行转义。Shell中使用了空格转义,但它没有使用八进制字符转义。

类似C语言风格的转义可以通过命令printf %b $escaped来撤销。(文档中说八进制转义以\0开头,但GNU printf好像并不需要这个)。另一个答案提到了使用read来取消Shell转义,但如果只有空格无法被printf %b处理,那么用sed处理这种情况可能更好。


“编码”基本上是bash字符转义(用于空格),但如果未设置编码ENV,则会将UTF-8字符的原始八进制数放入其中。 - Amandasaurus

1
最终我使用了类似这样的代码:
cat file | sed 's/%/%%/g' | while read -r line ; do printf "${line}\n" ; done | sed 's/\\ / /g'

一些文件中包含了%,这是printf的特殊字符,因此我不得不将其“加倍”,以便它被转义并直接传递。在read中的-r停止了read转义\,但是read不会将"\ "转换为" ",因此我需要最后的sed


2
如果您使用 printf "%b\n" $line,那么它将不会解释 $line 中的 % - mark4o

1
使用printf解决UTF-8文本问题。使用read处理空格(\ ),如下所示:
$ text='/My\ Folders/My\ r\303\266m/'
$ IFS='' read t < <(printf "$text")
$ echo "$t"
/My Folders/My röm/

0

将文件(逐行)传递到以下 Perl 脚本。

#!/usr/bin/per

sub encode {
    $String = $_[0];
    $_ = $String;
    while(/(\\[0-9]+|.)/g) {
        $Match = $1;

        if ($Match =~ /\\([0-9]+)/) {
            $Code = oct(0 + $1);
            $Char = ((($Code >= 32) && ($Code  160))
                ? chr($Code)
                : sprintf("\\x{%X}", $Code);
            printf("%s", $Char);
        } else {
            print "$Match";
        }
    }

    print "\n";
}

while ($#ARGV >= 0) {
    $File = shift();
    open(my $F, ") {
        $String =~ s/\\ / /g;
        &encode($Line);
    }
}

就像这样:

$ ./PerlEncode.pl Test.txt

Test.txt 包含以下内容:

/My\ Folders/My\ r\303\266m/
/My\ Folders/My\ r\303\266m/
/My\ Folders/My\ r\303\266m/

这行代码"$String =~ s/\ / /g;"将"\ "替换为" ",并使用sub encode解析这些Unicode字符。

希望这可以帮到你。


0

内置的“read”函数将处理部分问题:

$ echo "with\ spaces" | while read r; do echo $r; done
with spaces

那是我的第一次尝试,它确实处理了空格,但没有进行UTF8转换。例如,$ echo "with\ spaces \303\266" | while read r ; do echo $r ; done with spaces 303266 - Amandasaurus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接