传家宝工具箱 tr:尝试删除包含多字节字符的集合的补集时出错?

6 unicode tr

我试图使用tr命令从传家宝工具箱中,克服了电流限制的的coreutils的实施,以便能够“泵”(与-dc期权)从“随机”生成多字节字符(的/ dev / urandom的)到终端。值得注意的是,这是在使用 AUR版本失败后在Archbang上从源代码编译的事实。

为了简化这一点,让我们选择一个字符 (?) 并计算出它的八进制值,因为这是必须为 toolchest 表示的方式tr

echo '?' | hexdump -b            # -b for octal
0000000 342 230 240 012                                                
0000004
echo -e '\0342\0230\0240'        # uses the "0nnn" format, make sure it prints
?
Run Code Online (Sandbox Code Playgroud)

与此处的工具箱( nnn )相比,使用内置函数( 0nnn )在Bash 中表示八进制值的方式有所不同:echo tr

字符“\”后跟 1、2 或 3 个八进制数字代表字节码由这些数字给出的字符。多字节字符可以指定为八进制字节序列。

让我们试试吧。该-dc选项只是删除 SET1 的补码。您指定一个集合,标准输入中不包含集合中元素的任何内容都将被丢弃:

echo '012345' | /usr/5bin/tr -dc '456'   #sanity check
45                                       #all good
Run Code Online (Sandbox Code Playgroud)

现在这些:

echo -e '\0342\0230\0240' | /usr/5bin/tr -dc '\342\230\240'
echo -e '?' | /usr/5bin/tr -dc '?'
Run Code Online (Sandbox Code Playgroud)

哪个都应该打印 one(1)?,或者最终以下(更多字符)都会产生相同的错误:

/usr/5bin/tr -dc '\342\230\240' < /dev/urandom

*** Error in `/usr/5bin/tr': double free or corruption (!prev): 0x0000000000d24420 ***
Run Code Online (Sandbox Code Playgroud)

实际上,每次输入和 SET1 都包含所选字符时,错误会出现-dc。该行为在工具箱中提供的命令的SysV 3rd、4th、Posix、Posix2001 或 ucb(BSD)版本中也是相同的。有时,就像tr -dc '1' < /dev/urandom我得到正确的段错误或几行输出的情况一样:

Error in `/usr/5bin/tr': realloc(): invalid pointer: 0x00007f93ee284010 ***
======= Backtrace: =========
/usr/lib/libc.so.6(+0x73f8e)[0x7f93ee338f8e]
/usr/lib/libc.so.6(+0x7988e)[0x7f93ee33e88e]
/usr/lib/libc.so.6(realloc+0x1c8)[0x7f93ee342918]
/usr/5bin/tr[0x401a74]
/usr/5bin/tr[0x400e93]
/usr/lib/libc.so.6(__libc_start_main+0xf0)[0x7f93ee2e5000]
/usr/5bin/tr[0x400f63]
======= Memory map: ========
00400000-00403000 r-xp 00000000 08:21 1579535                            /usr/5bin/tr
00602000-00603000 rw-p 00002000 08:21 1579535                            /usr/5bin/tr
0067a000-006bc000 rw-p 00000000 00:00 0                                  [heap]
7f93edc6e000-7f93edc84000 r-xp 00000000 08:21 1448153                    /usr/lib/libgcc_s.so.1
7f93edc84000-7f93ede83000 ---p 00016000 08:21 1448153                    /usr/lib/libgcc_s.so.1
7f93ede83000-7f93ede84000 rw-p 00015000 08:21 1448153                    /usr/lib/libgcc_s.so.1
7f93ede84000-7f93ee2c5000 rw-p 00000000 00:00 0 
7f93ee2c5000-7f93ee469000 r-xp 00000000 08:21 1440453                    /usr/lib/libc-2.19.so
7f93ee469000-7f93ee669000 ---p 001a4000 08:21 1440453                    /usr/lib/libc-2.19.so
7f93ee669000-7f93ee66d000 r--p 001a4000 08:21 1440453                    /usr/lib/libc-2.19.so
7f93ee66d000-7f93ee66f000 rw-p 001a8000 08:21 1440453                    /usr/lib/libc-2.19.so
7f93ee66f000-7f93ee673000 rw-p 00000000 00:00 0 
7f93ee673000-7f93ee694000 r-xp 00000000 08:21 1440340                    /usr/lib/ld-2.19.so
7f93ee6eb000-7f93ee874000 r--p 00000000 08:21 1448356                    /usr/lib/locale/locale-archive
7f93ee874000-7f93ee877000 rw-p 00000000 00:00 0 
7f93ee891000-7f93ee893000 rw-p 00000000 00:00 0 
7f93ee893000-7f93ee894000 r--p 00020000 08:21 1440340                    /usr/lib/ld-2.19.so
7f93ee894000-7f93ee895000 rw-p 00021000 08:21 1440340                    /usr/lib/ld-2.19.so
7f93ee895000-7f93ee896000 rw-p 00000000 00:00 0 
7fffed79c000-7fffed7bd000 rw-p 00000000 00:00 0                          [stack]
7fffed7e9000-7fffed7eb000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Run Code Online (Sandbox Code Playgroud)

所有这一切都表明我有编译错误还是我没有正确使用它?


使用贡献的补丁,我们有:

echo -e '\0342\0230\0240' | /home/me/bin/trsc -dc '\342\230\240'
echo -e '?' | /home/me/bin/trsc -dc '?'
?
Run Code Online (Sandbox Code Playgroud)

正如我们应该的那样!!但:

/home/me/bin/trsc -dc '\342\230\240' < /dev/urandom
Run Code Online (Sandbox Code Playgroud)

仍然是个谜,因为选择的角色不在输出中......

Sté*_*las 7

我以前见过。一个错误。尝试:

--- tr.c        6 Sep 2005 23:04:11 -0000       1.10
+++ tr.c        30 May 2014 09:46:33 -0000
@@ -291,7 +291,6 @@
                if(c<ccnt) code[c] = d;
                if(d<ccnt && sflag) squeez[d] = 1;
        }
-       free(vect);
        while((d = next(&string2)) != NIL) {
                if(sflag) squeez[d] = 1;
                if(string2.max==NIL && (string2.p==NULL || *string2.p==0))
Run Code Online (Sandbox Code Playgroud)

(这是几个月前的快速浏览,虽然此补丁可以帮助您前进,但我不能保证它是正确的。使用 申请patch -l)。

现在还要注意/dev/urandom提供了一个字节流。在 UTF-8 中,并非所有字节序列都映射到有效字符。例如,0x41 0x81 0x41 是无效的,因为它0x81>=0x80,所以它只能出现在超过 0x80 字节的 2 个或更多的序列中。

无效字节,因为它不在作为 ? 的补码的字符集中,不会被 删除tr

更好的可能是:

recode ucs-2..u8 < /dev/urandom | tr -cd ?
Run Code Online (Sandbox Code Playgroud)

ucs-2 是字符 U+0000 到 U+FFFF,以每个字符 2 个字节编码,/dev/urandom看起来更像是 ucs-2 字符流。(虽然我们缺少字符 U+10000 到 U+10FFFF)。

但这仍然包括D800..DFFF 代理对范围 ,它mbrtowc(3)会阻塞(至少在我的 libc 版本中)。

这些代码点是为 UTF-16 编码而保留的。例如,d800dc00 是 U+10000 的 UTF-16BE 编码,但没有 U+D800 字符或 U+DC00。那些的 UTF-8 编码作为字符也没有意义(即使相邻)。

所以你需要先排除它们:

perl -ne 'BEGIN{$/=\2;binmode STDOUT,":utf8"}
          $c = unpack("n",$_); if ($c < 0xd800 || $c > 0xdfff) {
            no warnings "utf8"; print chr($c)
          }' < /dev/urandom | tr -cd ?
Run Code Online (Sandbox Code Playgroud)

如果重点是获取以 UTF-8 编码的随机 Unicode 字符流,那么最好在允许范围(0..0xd7ff、0xf000..0x10ffff)中获取随机代码点并将其转换为 UTF-8。如果你想基于它/dev/urandom,你可以为每个代码点使用 3 个字节(24 位):

perl -ne 'BEGIN{$/=\3;binmode STDOUT,":utf8"}
          $c = unpack("N","\0$_") * 0x10F800 >> 24;
          $c+=0x800 if $c >= 0xd800;
          do {no warnings "utf8"; print chr($c)}' < /dev/urandom |
  tr -cd ?
Run Code Online (Sandbox Code Playgroud)