【问题标题】:how to create a non-ascii file using redirection with `sh -c`?如何使用带有`sh -c`的重定向创建非ascii文件?
【发布时间】:2011-07-06 19:37:25
【问题描述】:

相同的命令:echo 1 > filename 创建不同的文件名:

$ sh -c 'echo $LANG >=с=.sh' && ls *.sh | od -c
0000000   = 321   =   .   s   h  \n
0000007

$ bash -c 'echo $LANG >=с=.bash' && ls *.bash | od -c
0000000   = 321 201   =   .   b   a   s   h  \n
0000012

其中сU+0441 字符——CYRILLIC SMALL LETTER ES。很明显sh 吃掉了utf-8 编码中的第二个字节。

$ ls *sh
=?=.sh  =с=.bash

$LANG 在这两种情况下都是:

$ cat *sh
en_US.utf8
en_US.utf8

sh 链接到我系统上的dash

$ apt-cache show dash | grep -i version
Version: 0.5.5.1-7ubuntu1

stty iutf8 已设置。

是否有任何设置允许dash 不破坏多字节字符?

我在手册中没有看到任何关于字符编码的提及:

$ man dash | grep -i encoding
$ man dash | grep -Pi 'multi.*byte'

更新

utf-8 编码中'с' 字符的第二个字节'\201'-127 在 C 中作为有符号字符(或 129 作为无符号字符)。

在源代码 (apt-get source dash) 中搜索 -127 的结果是:

src/parser.h:38:#define CTL_FIRST -127      /* first 'special' character */
src/parser.h:39:#define CTLESC -127     /* escape next character */

搜索CTLESC 会导致rmescapes() 宏导致 来自src/expand.c:expandarg()的以下片段:

/*
 * TODO - EXP_REDIR
 */
if (flag & EXP_FULL) {
    ifsbreakup(p, &exparg);
    *exparg.lastp = NULL;
    exparg.lastp = &exparg.list;
    expandmeta(exparg.list, flag);
} else {
    if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
        rmescapes(p);
    sp = (struct strlist *)stalloc(sizeof (struct strlist));
    sp->text = p;
    *exparg.lastp = sp;
    exparg.lastp = &sp->next;
}

TODOXXX 暗示更新的版本可能 帮助。 debian/dash.README.source 指向:

$ git clone http://smarden.org/git/dash.git/
$ cd dash

有两个分支:

$ git br
* debian-sid
  release+patches

debian-sid 上,转义字节被删除。在release+patches 分支grep 找到丢失的字节。

$ ./configure
$ make && rm *.dash -f; ./dash -c 'echo 1 >fсf.dash' && 
> ls *.dash | od -c | grep 201

git diff debian-sid...release+patches 表明 rmescapes() 是 在release-patches中删除:

diff --git a/src/expand.c b/src/expand.c
index e4c4c8b..f2f964c 100644
--- a/src/expand.c
+++ b/src/expand.c
...
@@ -213,8 +210,6 @@ expandarg(union node *arg, struct arglist *arglist, int flag)
                exparg.lastp = &exparg.list;
                expandmeta(exparg.list, flag);
        } else {
-               if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
-                       rmescapes(p);
                sp = (struct strlist *)stalloc(sizeof (struct strlist));
                sp->text = p;
                *exparg.lastp = sp;
@@ -412,7 +407,7 @@ lose:
 }

尚不清楚这些更改是否会包含在 Ubuntu 上的 dash 0.5.6.1 中。

目前唯一的命令方式:

$ sh -c 'echo 1 >fсf.dash' &&  ls *.dash | od -c | grep 201

工作是将sh重新配置回bash

$ sudo dpkg-reconfigure dash

还有其他选择吗?

【问题讨论】:

    标签: ubuntu utf-8 debian sh dash-shell


    【解决方案1】:

    在我尝试的几个 shell(或版本)中,只有 Dash 和 Busybox Ash 失败了。

    $ for sh in bash2.05b bash3.2 bash4.0 bash4.1 bash4.2 dash zsh ksh pdksh mksh ash; do $sh -c 'locale > с.$0'; done
    $ csh -c 'locale > с.csh'
    $ fish -c 'locale > с.fish'
    $ ls -1
    ?.ash
    ?.dash
    с.bash2.05b
    с.bash3.2
    с.bash4.0
    с.bash4.1
    с.bash4.2
    с.csh
    с.fish
    с.ksh
    с.mksh
    с.pdksh
    с.zsh
    

    内容都一样。

    来自man dash

    只有 POSIX 指定的功能,以及一些 Berkeley 扩展,才会被合并 进入这个外壳。本手册页并非旨在作为教程或 完整的外壳规范。

    POSIX 说:

    POSIX 语言环境包含 Portable Character Set 中的字符,这些字符具有 LC_CTYPE 中列出的属性。在其他语言环境中,任何其他字符的存在、含义和表示都是特定于语言环境的。

    其他字符的宽字符代码是区域设置和实现定义的。 ... POSIX.1-2008 没有提供定义宽字符代码集的方法。

    【讨论】:

    • dash 包 git 存储库 smarden.org/git/dash.git 中的第二个分支 release+patches 有效,但默认的 debian-sid 无效。我已经用详细信息更新了问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-09
    • 2021-11-22
    • 1970-01-01
    • 2018-01-12
    • 2011-07-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多