Трабл с обратной косой чертой в Tcl exec - PullRequest
0 голосов
/ 01 февраля 2019

Я пишу сценарий импорта данных в Tcl (от SQL Server до Postgres) и должен вызвать командную строку unix tr, чтобы удалить нулевые символы в файле данных.Я записываю данные во временный файл, а затем использую exec для обработки файла через tr.

. Вызов tr, который я хотел бы генерировать с помощью Tcl, выглядит следующим образом в командной строке:

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

Код Tcl, который я использую для создания вышеприведенного кода, таков: $STATE(TMP) содержит временный файл:

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

Однако иногда это не работает, и PostgreSQL COPY завершается неудачноиз-за х00 символов.Если я запускаю версию командной строки для файла, то 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-сервера и т. Д., Поскольку все стандартные инструменты не работают с размером наших данных.)

Благодарявсем кто читает или ответит!

1 Ответ

0 голосов
/ 01 февраля 2019

Дело в том, что 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, чтобы изменить положение вещей, но не будет большой разницы в производительности.

...