Итак, у меня есть файл, содержащий поля с разделителями ~
, которые мне нужно преобразовать в sql. Загвоздка здесь в том, что в этих данных есть пробелы. Как правило, это не было бы проблемой, потому что я мог бы просто использовать xargs -0
и передать ему нулевые разделители.
Когда я соединяю его с tr '~' '\0000'
, я веду себя странно, и я не знаю, почему. Кажется, что при использовании xargs на самом деле требуется слишком много полей.
Вход:
FA_PRD01_PHX1~EBDT~30-JAN-2023~18~JobDefinition://company/apps/ess/custom/Shared Folders/Apps_Reports/APPSSALESFORCETIMEDTLS~1~1
FA_PRD01_PHX1~EBDT~30-JAN-2023~08~JobDefinition://company/apps/ess/custom/Shared Folders/Apps_Reports/APPSSALESFORCETIMEDTLSNEW~1~1
FA_PRD01_PHX1~EBDT~23-JAN-2023~18~JobDefinition://company/apps/ess/custom/Shared Folders/Apps_Reports/APPSSALESFORCETIMEDTLS~1~1
FA_PRD01_PHX1~EBDT~23-JAN-2023~08~JobDefinition://company/apps/ess/custom/Shared Folders/Apps_Reports/APPSSALESFORCETIMEDTLSNEW~1~1
FA_PRD01_PHX1~EBDT~13-MAR-2023~04~JobDefinition://company/apps/ess/custom/shared/Apps_Reports/Certifications and Competencies/ALLCERTRPT~1~1
FA_PRD01_PHX1~EBDT~13-FEB-2023~18~JobDefinition://company/apps/ess/custom/Shared Folders/Apps_Reports/APPSSALESFORCETIMEDTLS~1~1
FA_PRD01_PHX1~EBDT~13-FEB-2023~08~JobDefinition://company/apps/ess/custom/Shared Folders/Apps_Reports/APPSSALESFORCETIMEDTLSNEW~1~1
FA_PRD01_PHX1~EBDT~13-FEB-2023~05~JobDefinition://company/apps/ess/custom/shared/Apps_Reports/Certifications and Competencies/ALLCERTRPT~1~1
FA_PRD01_PHX1~EBDT~06-FEB-2023~18~JobDefinition://company/apps/ess/custom/Shared Folders/Apps_Reports/APPSSALESFORCETIMEDTLS~1~1
FA_PRD01_PHX1~EBDT~06-FEB-2023~08~JobDefinition://company/apps/ess/custom/Shared Folders/Apps_Reports/APPSSALESFORCETIMEDTLSNEW~1~1
команда:
cat data.txt | tr '~' '\0000' |
xargs -0 -n 7 printf "insert into ESS_SYSTEM_ERROR_METRICS values('%s','%s',to_date('%s','DD-MM-YYYY'),%s,'%s',%s,%s);\n"
ожидал:
insert into ESS_SYSTEM_ERROR_METRICS values('FA_PRD01_PHX1','EBDT',to_date('30-JAN-2023','DD-MM-YYYY'),18,'JobDefinition://company/apps/ess/custom/Shared Folders/Apps_Reports/APPSSALESFORCETIMEDTLS',1,1);
действительный:
insert into ESS_SYSTEM_ERROR_METRICS values('FA_PRD01_PHX1','EBDT',to_date('30-JAN-2023','DD-MM-YYYY'),18,'JobDefinition://company/apps/ess/custom/Shared Folders/Apps_Reports/APPSSALESFORCETIMEDTLS',1,1
FA_PRD01_PHX1);
insert into ESS_SYSTEM_ERROR_METRICS values('EBDT','30-JAN-2023',to_date('08','DD-MM-YYYY'),JobDefinition://company/apps/ess/custom/Shared Folders/Apps_Reports/APPSSALESFORCETIMEDTLSNEW,'1',1
FA_PRD01_PHX1,EBDT);
Мой счетчик полей с xargs правильный, поэтому мне также интересно, использует ли xargs что-то еще для разграничения.
xargs
использует нулевой байт для разграничения, как вы сказали. Это не разграничивает новые строки.
В очень упрощенном примере, если у вас есть эти две строки (где 0
— ноль):
a0b0c0d\n
e0f0g0h\n
Четвертый токен — это не просто d
, это d\ne
, потому что xargs
читается до следующего нулевого байта.
Что-то вроде этого должно работать:
while IFS='~' read -ra line ; do
printf '%s\0' "${line[@]}"
done < data.txt | xargs -0 -n 7 printf "insert into ESS_SYSTEM_ERROR_METRICS values('%s','%s',to_date('%s','DD-MM-YYYY'),%s,'%s',%s,%s);\n"
нет необходимости в \0
+ xargs
в этом случае; можно передать массив прямо в нужный printf
внутри цикла: while IFS='~' read -ra line; do printf "insert into ...." "${line[@]}"; done < data.txt
Поскольку вы заменяете ~
символом \0
и используете \0
в качестве разделителя для xargs
, \n
во входных данных будет рассматриваться как часть последнего «поля»; вам также нужно перевести новые строки на входе:
tr '\n~' '\0' < data.txt |
xargs -0 -n 7 printf "insert into ESS_SYSTEM_ERROR_METRICS values('%s','%s',to_date('%s','DD-MM-YYYY'),%s,'%s',%s,%s);\n"
совет: при автоматической генерации кода вам лучше правильно экранировать строки (здесь вы печатаете их RAW, это может оказаться опасным)
Под правильным экранированием я подразумеваю генерацию SQL таким образом, чтобы не допустить внедрения кода; например:
awk -F'~' '
function sql_stringify(str,escape_backslashes) {
gsub(/\047/,"\047\047",str);
if (escape_backslashes)
gsub(/\/,"\\\\&",str);
return "\047" str "\047";
}
{
printf( "insert into ESS_SYSTEM_ERROR_METRICS " );
printf( "values(%s,%s,to_date(%s,%s),%d,%s,%d,%d);\n", \
sql_stringify($1), \
sql_stringify($2), \
sql_stringify($3), \
sql_stringify("DD-MM-YYYY"), \
$4, \
sql_stringify($5), \
$6, \
$7 \
);
}
' data.txt
note: for MySQL and PostgreSQL you'll need to set the escape_backslashes
switch of sql_stringify
to true
Я решил использовать %s
вместо %d
, потому что printf считает, что 09
следует интерпретировать как восьмеричное.
Поскольку xargs
рассматривает \0
как разделитель, \n
становится частью данных. В идеале нам также нужно преобразовать \n
в \0
. В качестве альтернативы ...
Мы можем сохранить разделитель ~
и преобразовать \n
в ~
:
$ head -2 data.txt | tr '\n' '~' | xargs -d~ -n 7 printf "insert into ESS_SYSTEM_ERROR_METRICS values('%s','%s',to_date('%s','DD-MM-YYYY'),%s,'%s',%s,%s);\n"
insert into ESS_SYSTEM_ERROR_METRICS values('FA_PRD01_PHX1','EBDT',to_date('30-JAN-2023','DD-MM-YYYY'),18,'JobDefinition://company/apps/ess/custom/Shared Folders/Apps_Reports/APPSSALESFORCETIMEDTLS',1,1);
insert into ESS_SYSTEM_ERROR_METRICS values('FA_PRD01_PHX1','EBDT',to_date('30-JAN-2023','DD-MM-YYYY'),08,'JobDefinition://company/apps/ess/custom/Shared Folders/Apps_Reports/APPSSALESFORCETIMEDTLSNEW',1,1);
Альтернативная идея с использованием awk
:
$ awk -F'~' -v qt = "'" '{printf "insert into ESS_SYSTEM_ERROR_METRICS values(%s,%s,to_date(%s,%s),%s,%s,%s,%s);\n",qt $1 qt,qt $2 qt,qt $3 qt,qt "DD-MM-YYYY" qt, $4,qt $5 qt,$6,$7 }' data.txt
insert into ESS_SYSTEM_ERROR_METRICS values('FA_PRD01_PHX1','EBDT',to_date('30-JAN-2023','DD-MM-YYYY'),18,'JobDefinition://company/apps/ess/custom/Shared Folders/Apps_Reports/APPSSALESFORCETIMEDTLS',1,1);
insert into ESS_SYSTEM_ERROR_METRICS values('FA_PRD01_PHX1','EBDT',to_date('30-JAN-2023','DD-MM-YYYY'),08,'JobDefinition://company/apps/ess/custom/Shared Folders/Apps_Reports/APPSSALESFORCETIMEDTLSNEW',1,1);
... snip ...
Конечно, в этом конкретном случае это немного излишне, но кое-что следует иметь в виду, если / когда вы столкнетесь с некоторыми сложными проблемами форматирования, которые трудно (или) решить с помощью только tr/xargs
, ymmv ...
xargs -d
плохо поддерживается
-d
не поддерживается на вашем Mac.
Я не знал, что xargs по-прежнему будет использовать \n
в качестве разделителя, если я скажу это -0
, это была моя ошибка
использовать
awk -F~ '{printf("insert ... %s.....%s\n", $1, $2... $7)'
вместо этого? (тоже не нужноxargs
(?) Удачи.