【问题标题】:Trouble with backslash in Tcl execTcl exec中的反斜杠问题
【发布时间】:2019-02-01 03:42:03
【问题描述】:

我正在用 Tcl 编写一个数据导入脚本(从 SQL Server 到 Postgres),并且必须调用命令行 unix tr 来清除数据文件中的空字符。我将数据写入临时文件,然后使用exec 通过tr 处理文件。

我希望 Tcl 生成的 tr 调用在命令行上如下所示:

tr -d '\000' < blah >blah.notnull

我用来制作上面的 Tcl 代码是这样的,$STATE(TMP) 保存临时文件:

set ret [catch {exec tr -d '\\000' < $STATE(TMP) > $STATE(TMP).clean}]

但是,有时这不起作用并且 PostgreSQL COPY 由于 x00 个字符而失败。如果我在文件上运行命令行版本,则 COPY 成功。

有人可以帮助我理解 exec 调用以及引用和反斜杠吗?我有点难过。

错误信息,PG错误的重新格式化版本:

Problem with COPY on blahblah: PGRES_FATAL_ERROR, ERROR:  invalid byte sequence for encoding "UTF8": 0x00

令人讨厌的是,Tcl exec 代码经常有效,但并非总是如此。

(我们正在使用 Tcl、Linux、BCP、SQL 服务器等手动滚动导入系统,因为所有现成的工具都因我们的数据大小而失败。)

感谢所有阅读或回答的人!

【问题讨论】:

    标签: postgresql tcl exec


    【解决方案1】:

    问题是 Tcl 根本没有赋予单引号任何特殊含义。 Tcl 中的等价物是大括号,所以使用{\000} 而不是'\000'。使用您所写的内容,您发送了三个字符(一个'、一个NUL 和另一个')作为该参数,这会导致各种麻烦,因为文字NUL 字符不如C 字符串好。

    因此,您应该这样做:

    exec tr -d {\000} < blah >blah.notnull
    

    或:

    set ret [catch {
        exec tr -d {\000} < $STATE(TMP) > $STATE(TMP).clean
    }]
    

    Tcl 也可以直接进行该操作。

    # Read binary data
    set f [open $STATE(TMP) "rb"]
    set data [read $f]
    close $f
    
    # Write transformed binary data
    set f [open $STATE(TMP).clean "wb"]
    puts -nonewline $f [string map [list \u0000 ""] $data]
    close $f
    

    [编辑]:当要转换的数据量很大时,最好一次做一点。

    set fIn [open $STATE(TMP) "rb"]
    set fOut [open $STATE(TMP).clean "wb"]
    while true {
        # 128kB chunk size; a bit arbitrary, but big enough to be OK
        set data [read $fIn 131072]
        # If we didn't read anything and instead got EOF, stop the loop
        if {[eof $fIn]} break
        puts -nonewline $fOut [string map [list \u0000 ""] $data]
    }
    close $fIn
    close $fOut
    

    您也可以使用 Tcl 8.6 通道转换来完成这项工作,然后 fcopy 将内容移过来,但性能不会有太大差异。

    【讨论】:

    • Donal - 有没有办法捕获 exec 正在执行的插入字符串?您的代码运行没有错误,但我无法获得最终想要的效果,并且我的 postgres COPY 仍然失败;我希望看看它是如何执行的。而我不做直接Tcl版本的原因是因为文件通常是15G左右。发送!
    • 啊,对于 15GB,您想要流式传输或分块。现在很容易做到,因为我知道这是一个问题。可以这么说,字符串就是它在锡上所说的;有三个单词:tr-d\000(它们在 Unix 上不是一个字符串;单词边界在所有 POSIX 平台上都保留)。剩下的就是重定向。
    • 感谢多纳尔的帮助!我仍然想知道如何更好地诊断通过 exec 传递的 shell 调用。即使我们在这里不使用它,我将来也会将 shell 用于其他事情,而且我知道运行您的命令与命令行版本会产生不同的结果,所以我想深入了解它.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-25
    相关资源
    最近更新 更多