【问题标题】:How to copy files with special characters in their names with TCL's exec?如何使用 TCL 的 exec 复制名称中带有特殊字符的文件?
【发布时间】:2019-08-29 06:21:02
【问题描述】:

我正在尝试通过exec 命令在我们的平台上上传包含特殊字符的文件,但这些字符总是被解释并且失败了。

例如,如果我尝试上传 mémo.txt 文件,则会收到以下错误:

/bin/cp: 无法创建常规文件`/path/to/dir/m\351mo.txt': 没有这样的文件或目录

系统上正确配置了 UTF8,如果我在 shell 上运行该命令,它工作正常。

这是 TCL 代码: exec /bin/cp $tmp_filename $dest_path

我怎样才能让它工作?

【问题讨论】:

    标签: file copy tcl centos6 aolserver


    【解决方案1】:

    问题的核心是使用何种编码与操作系统进行通信。对于exec 和文件名,该编码是encoding system command 返回的任何内容(Tcl 很好地猜测了 Tcl 库启动时的正确值,但偶尔会出错)。在我的计算机上,该命令返回 utf-8,它表示(正确!)传递给操作系统(和从操作系统接收)的字符串是 UTF-8。

    您应该能够使用file copy 命令而不是使用exec /bin/cp,这在这里会很有帮助,因为它减少了复杂的层级(它避免了通过可能强加其自身问题的外部程序)。我们假设已经完成了:

    set tmp_filename "foobar.txt";  # <<< fill in the right value, of course
    set dest_path "/path/to/dir/mémo.txt"
    file copy $tmp_filename $dest_path
    

    如果失败,我们需要找出原因。最可能的问题与编码有关,并且可能以多种可怕的交互方式出错。唉,细节很重要。特别是,路径的编码取决于实际的文件系统(它是创建文件系统时的正式参数),并且当您在另一个挂载中有一个挂载时,路径的各个部分之间的 Unix 可能会有所不同。

    如果最坏的情况发生,您可以将 Tcl 置于 ISO 8859-1 模式,然后自己进行所有编码(因为 ISO 8859-1 是“只使用我告诉你的字节”编码); encoding convertto 在这种情况下也很有用。请注意,这可能会生成给其他程序带来麻烦的文件名,但至少可以让您解决问题。

    encoding system iso98859-1
    file copy $tmp_filename [encoding convertto utf-8 $dest_path]
    

    在这种情况下,可能需要注意正确转换路径的不同部分:您对发生的事情负全部责任。


    如果您使用的是 Windows,请让 Tcl 处理细节。 Tcl 直接使用 Wide (Unicode) Windows API,因此您可以假装不存在这些问题。 (还有其他个问题。)

    在 macOS 上,请不要理会encoding system,因为它正确的。 Mac 对编码有一种非常固执的方法。

    【讨论】:

    • 非常感谢您的详细解答! encoding system 命令返回 iso8859-1。这是否意味着后端操作系统(CentOS 6)配置不正确,或者操作系统(Windows 10)配置正确?我已经尝试过file copy 命令,但它说error copying "/tmp/file7k5kqg" to "/path/to/dir/mémo.txt": no such file or directory...跨度>
    • 命令file copy $tmp_filename [encoding convertto utf-8 $dest_path] 确实有效!
    • 最后一段代码有一个小错字:iso98859-1 ==> iso8859-1
    • 最后我可以通过在文件存储模块中设置encoding system utf-8 使其工作。
    【解决方案2】:

    我已经尝试了文件复制命令,但它说复制错误 “/tmp/file7k5kqg”到“/path/to/dir/mémo.txt”:没有这样的文件或 目录

    我对您的问题的解读是,出于某种原因,您的 Tcl 设置为 iso8859-1 ([encoding system]),而执行环境 (shell) 设置为 utf-8。这解释了为什么 Donal 的建议对您有用:

    encoding system iso8859-1
    file copy $tmp_filename [encoding convertto utf-8 $dest_path]
    

    这将安全地将utf-8 编码的字节数组传递给任何系统调用:é\xc3\xa9\u00e9。观看:

    % binary encode hex [encoding convertto utf-8 é] 
    c3a9
    % encoding system iso8859-1; exec xxd << [encoding convertto utf-8 é] 
    00000000: c3a9                                     ..
    

    这相当于[encoding system] 也被设置为utf-8(正如在utf-8 环境中所预期的那样):

    % encoding system
    utf-8
    % exec xxd << é
    00000000: c3a9                                     ..
    

    您正在经历的(没有任何干预)似乎是在从 Tcl 退出时将 Tcl 内部编码重新编码为 iso8859-1(因为 [encoding system],正如 Donal 所描述的那样),以及后续 -将此iso8859-1 值重新编码(和错误)到utf-8 环境中。

    注意区别(\xe9\xc3\xa9):

    % encoding system iso8859-1
    % encoding system
    iso8859-1
    %  exec xxd << é
    00000000: e9
    

    问题似乎是 \xe9 将在您的其他 utf-8 环境中解释,例如:

    $ locale
    LANG="de_AT.UTF-8"
    ...
    $ echo -ne '\xe9'
    ?
    $ touch `echo -ne 'm\xe9mo.txt'`
    touch: m?mo.txt: Illegal byte sequence
    $ touch mémo.txt
    $ ls mémo.txt 
    mémo.txt
    $ cp `echo -ne 'm\xe9mo.txt'` b.txt
    cp: m?mo.txt: No such file or directory
    

    但是:

    $ cp `echo -ne 'm\xc3\xa9mo.txt'` b.txt
    $ ls b.txt
    b.txt
    

    您的选择:

    (1) 首先,您需要找出 Tcl 选择iso8859-1 的原因。您是如何获得安装的?自编?详情(版本)是什么?

    (2) 您可以按照 Donal 的建议进行操作,或者明确设置 encoding system utf-8

    encoding system utf-8
    file copy $tmp_filename $dest_path
    

    【讨论】:

    • 感谢您的解释!我可以按照您的建议设置 encoding system utf-8 使其工作。
    • 很高兴它有所帮助,但请注意,这只是一种解决方法,而不是永久修复。您需要找出为什么您的 Tcl 会初始化为 iso8859-1 而不是 utf-8 模式。
    • 是的,我会继续调查。我没有安装它,它包含在我们正在使用的这个应用程序中:project-open.com
    • 这是基于openacs.org,您可能需要联系他们的forums。另外,试试看你的config.tcl中是否有systemencoding的某个参数设置为iso8859-1
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-08
    • 1970-01-01
    • 2014-04-17
    • 1970-01-01
    • 2014-07-08
    • 1970-01-01
    相关资源
    最近更新 更多