我正在用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等手动滚动导入系统,因为所有现成的工具都会因数据大小而失败。)
感谢所有阅读或回答的人!
答案 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
进行移动,但是性能不会有太大差异。