У меня есть место назначения, которое использует CSV-файлы, и 6-е поле содержит слова, но максимальная длина символов составляет 16. Если длина поля превышает 16 символов, я хотел бы продублировать строку и разбить ее, не разбивая слова.
Текущий файл
"5","4","3","2","1","XYZ ABCD E"
"1","2","3","4","5","AB CDE F GHI JK LMNOP Q RS TUV W XYZ 12 3456 7890"
"9","8","7","6","5","LMN O PQ R"
Желаемый результат
"5","4","3","2","1","XYZ ABCD E"
"1","2","3","4","5","AB CDE F GHI JK"
"1","2","3","4","5","LMNOP Q RS TUV W"
"1","2","3","4","5","XYZ 12 3456 7890"
"9","8","7","6","5","LMN O PQ R"
решение1
Использование GNU Awk ( gawk
) для fold
запускаGetline/Переменная/Сопроцесс
gawk -F, '
BEGIN{
OFS=FS;
cmd="fold -sw 16";
}
# if total length (16 + 2 for quotes) is within limit, print as-is
length($NF) <= 18 {print; next}
# else
{
# trim the quotes, then fold
print substr($NF,2,length($NF)-2) |& cmd;
close(cmd,"to");
NF--;
while((cmd |& getline var) > 0){
# (optional) trim trailing whitespace
sub(/[ \t]+$/,"",var);
print $0, "\"" var "\"" ;
}
close(cmd,"from");
}
' file.csv
Удаляет sub
конечные пробелы из fold
операции.
Обратите внимание, что для получения точного вывода, показанного на рисунке, необходимо использовать fold -sw17
to break на 16 символах плюс (впоследствии удаленный) завершающий пробел. Однако, делая это, можно превысить ограничение в 16 символов в последней строке свернутого вывода.
решение2
Я создал довольно хромой скрипт awk, который сохраняет двойные кавычки. Вот он:
{
for ( i=0; i<= length($6); i+=16 )
{
if ( i+17 < length($6) )
{
if ( i == 0 )
printf ("%s,%s,%s,%s,%s,%s\"\n", $1, $2, $3, $4, $5, substr($6,i,16))
else
printf ("%s,%s,%s,%s,%s,\"%s\"\n", $1, $2, $3, $4, $5, substr($6,i+1,16))
}
else
{
if ( i == 0 )
printf ("%s,%s,%s,%s,%s,%s\n", $1, $2, $3, $4, $5, substr($6,i,16))
else
printf ("%s,%s,%s,%s,%s,\"%s\n", $1, $2, $3, $4, $5, substr($6,i+1,16))
}
}
}
Вывод:
$ awk -F, -f awks csvfields
"5","4","3","2","1","XYZ ABCD E"
"1","2","3","4","5","AB CDE F GHI JK"
"1","2","3","4","5"," LMNOP Q RS TUV "
"1","2","3","4","5","W XYZ 12 3456 78"
"1","2","3","4","5","90"
"9","8","7","6","5","LMN O PQ R"
$
Единственная проблема в том, что если на границе есть пробел, то он сохраняется, в отличие от примера, где он был удален.
решение3
Попробовал с кодом ниже, и он тоже отлично сработал
k=16;for ((j=1;j<=50;j++)); do awk -v j="$j" -v k="$k" -F "," '{if(length($NF) > 16){print $1,$2,$3,$4,$5,substr($NF,j,k)}else {print $0}}' filename; j=$(($j+16)); done|sort | uniq
выход
"5","4","3","2","1","XYZ ABCD E"
"1","2","3","4","5","AB CDE F GHI JK"
"1","2","3","4","5","LMNOP Q RS TUV W"
"1","2","3","4","5","XYZ 12 3456 7890"
"9","8","7","6","5","LMN O PQ R"
решение4
Подход SHELL (проверено на Bash и Ksh93). Однако мне нравится этот fold
подход, поскольку он использует существующий инструмент.
# read from stdin, output to stdout
# Note no Shebang line at top so it made it easier for to try bash/ksh as interpreters
OIFS="$IFS"
IFS=,
while read f1 f2 f3 f4 f5 f6; do
f6=${f6#\"}
f6=${f6%\"} # strip DQs
if ((${#f6}<17)); then # no action
IFS="$OIFS"
echo "$f1,$f2,$f3,$f4,$f5,\"$f6\""
IFS=","
continue
else
IFS="$OIFS"
while ((${#f6}>17)); do
n6=${f6:0:16}
f6=${f6#$n6}
n6=${n6# }
n6=${n6% }
echo "$f1,$f2,$f3,$f4,$f5,\"$n6\""
done
echo "$f1,$f2,$f3,$f4,$f5,\"${f6# }\""
fi
IFS=","
done
IFS="$OIFS"
exit
Полученные результаты:
"5","4","3","2","1","XYZ ABCD E"
"1","2","3","4","5","AB CDE F GHI JK"
"1","2","3","4","5","LMNOP Q RS TUV W"
"1","2","3","4","5","XYZ 12 3456 7890"
"9","8","7","6","5","LMN O PQ R"
Чтобы решить проблему переноса слов без использования fold
или подобного, следующий код должен заменить закомментированную строку, показанную ниже в приведенном выше коде. Также замените вторую echo
командную строку на:
c6="$f6"
n6=""
while (((${#n6}+${#nw})<=16)); do
n6=$n6${c6%% *}\
n6=${n6# }
eval c6=\${c6\#${c6%% *} }
nw=${c6%% *}
done
#n6=${f6:0:16} ### replace by above
и заменить
echo "$f1,$f2,$f3,$f4,$f5,\"${f6# }\""
с
((${#f6}>0)) && echo "$f1,$f2,$f3,$f4,$f5,\"${f6# }\""
чтобы избежать возникновения остатков нулевого поля 6.
Был использован следующий тестовый файл:
"5","4","3","2","1","XYZ ABCD E"
"1","2","3","4","5","AB CDE F GHI JK LMNOP Q RS TUV W XYZ 12 3456 7890"
"9","8","7","6","5","LMN O PQ R"
"1","2","3","4","5","A BB CCC DDD EEEE FFFFF GGGGGG HHHHHHH"
с результатами:
"5","4","3","2","1","XYZ ABCD E"
"1","2","3","4","5","AB CDE F GHI JK"
"1","2","3","4","5","LMNOP Q RS TUV W"
"1","2","3","4","5","XYZ 12 3456 7890"
"9","8","7","6","5","LMN O PQ R"
"1","2","3","4","5","A BB CCC DDD"
"1","2","3","4","5","EEEE FFFFF"
"1","2","3","4","5","GGGGGG HHHHHHH"
Использование существующих инструментов, таких как fold
however, намного проще и следует философии UNIX — надстройка над существующими простыми инструментами. Но если вам нравится программирование Shell, то вышеизложенное — один из способов получить решение. Если кому-то нужны объяснения кода, свяжитесь со мной.