Tcl exec中的反斜杠出现问题

时间:2019-02-01 03:42:03

标签: postgresql tcl exec

我正在用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}]

但是,有时这不起作用,由于x00字符,PostgreSQL COPY失败。如果我在文件上运行命令行版本,则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 Server等手动滚动导入系统,因为所有现成的工具都会因数据大小而失败。)

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

1 个答案:

答案 0 :(得分:0)

问题是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

[EDIT]:当要转换的数据量很大时,最好一次执行一点操作。

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进行移动,但是性能不会有太大差异。