У меня есть несколько файлов до миллиона записей, поступающих от поставщика.
Эти файлы изначально из среды Windows, и когда мы их получаем, они содержат ошибочные разбиения в записях, вызванные переводом строки в середине текстовых полей.
Я думаю, что это вызвано символами, которые интерпретируются как перевод строки во время передачи в Linux, но я не уверен, поскольку мы никогда не видим исходные файлы Windows.
Мне нужна подпрограмма, которая будет подсчитывать количество разделителей в строке и, если оно ниже заданного порога, удалить перевод строки в конце записи. Так, например, мы знаем, что запись состоит из 29 столбцов и должна иметь 28 разделителей «конвейер» («|»). Когда мы получаем данные из-за ошибочного перевода строки, мы получаем две записи, например, с 10 полями и 9 разделителями. и второй с 19 полями и 18 разделителями. Вот пример данных, измененных в целях безопасности:
9999999999|Duck Donald|87|||999999999|9999999999|XX999999|||Z99999|999 Planet Ln|||Trumpet
ville|ZZ|99999||||||ZZ|P|9999999999|F|||
Обратите внимание, что строка разделяется на слове Trumpetville. Это связано с тем, что был вставлен или неправильно переведен символ перевода строки. Снова ищем программу для подсчета разделителей и удаления перевода строки, если количество разделителей упадет ниже указанного порогового значения.
Try:
sed -e :1 -e 's/|/|/28;t' -e 'N;s/\n//;t1' < your-file
Or:
awk -F'|' '{while (NF < 29 && (getline nextline) > 0)
$0 = $0 nextline; print}' < your-file
В случае, если текст имеет разделители строк CRLF Microsoft, вы можете сначала обработать файлы с помощью dos2unix.
Я прочитал, что оригинальный ответ на самом деле не то, что вы ищете. Пожалуйста, ознакомьтесь со следующим сценарием. Это работает только тогда, когда вы заранее знаете количество элементов входного файла:
#!/bin/bash
infile=/home/wokie/duck.txt
outfile=/home/wokie/duck2.txt
# Define the amount of elements/columns in a row
maxelem=28
# Read the file, strip all newline characters and create one big variable
inputOneline=$(cat $infile | dos2unix | tr -d '\n')
count=0
# Read through the variable and split elements
for element in ${inputOneline//|/ }
do
if [ $count -lt $maxelem ]
then
# Write element to outfile while suppresing newline (-n)
echo -n "$element|" >> $outfile
count=$[$count +1]
else
# Write newline to outfile when maximum elements is reached
echo >> $outfile
count=0
fi
done
Вся идея состоит в том, чтобы сначала удалить все новые строки, после этого новый файл будет собран вместе с новыми строками в нужных местах.
Скрипт использует отличный инструмент под названием dos2unix. Запустив dos2unix только с именем файла в качестве параметра, он автоматически преобразует входной файл (Windows) в формат unix.
Если запустить инструмент с параметром -id, подсчитывается количество случаев CRLF, например:
[test@testsystem ~]$ dos2unix -id /home/wokie/test2.txt
5 /home/wokie/test2.txt
В файле примера 5 случаев.
Вы можете найти инструмент dos2unix здесь: https://sourceforge.net/projects/dos2unix/. Большинство дистрибутивов предоставляют dos2unix в стандартной комплектации или предоставляют возможность установки с помощью apt-get или dnf.
Предполагая, что нежелательный CRLF в данных не встречается в начале поля, вы можете:
Заменить предполагаемый CRLF в конец строки с Linux EOL, LF .
Это должно происходить после вертикальной черты. Эфир напрямую или через пробел. Поэтому замените строки «pipe space CRLF» и «pipe CRLF» на «pipe space LF» или «pipe LF»
0x7C 0x20 0x0D 0x0A
и 0x7C 0x0D 0x0A
на
0x7C 0x20 0x0A
и 0x7C 0x0A
Теперь единственный CRLF , который остался, находится в данных. Замените эту строку «CRLF» пробелом или нулем.
0x0D 0x0A
до 0x20
править:
Предполагаемый EOL должен быть между 2 трубами.
| данные | CRLF
| данные |
0x7C 0x0D 0x0A 0x7C