Удаление нежелательных символов и пустых строк с помощью SED, TR и/или awk

Мне нужно удалить некоторые неизвестные символы и оставшиеся пустые строки из файла, это должно быть просто, и я чувствую себя очень глупо, что я еще не мог этого сделать.

Вот содержимое файла (читаемое):

    136;2014-09-07 13:41:25;2014-09-07 13:41:55
    136;2014-09-07 13:41:55;2014-09-07 13:42:25
    136;2014-09-07 13:42:25;2014-09-07 13:42:55
    (empty line)
    (empty line)

По какой-то причине этот файл содержит несколько нежелательных/неизвестных символов. HEX это:

    fffe 3100 3300 3600 3b00 3200 3000 3100 3400 2d00 3000 3900  :..1.3.6.;.2.0.1.4.-.0.9.
    2d00 3000 3700 2000 3100 3300 3a00 3400 3100 3a00 3200 3500  :-.0.7. .1.3.:.4.1.:.2.5.
    3b00 3200 3000 3100 3400 2d00 3000 3900 2d00 3000 3700 2000  :;.2.0.1.4.-.0.9.-.0.7. .
    3100 3300 3a00 3400 3100 3a00 3500 3500 0d00 0a00 3100 3300  :1.3.:.4.1.:.5.5.....1.3.
    3600 3b00 3200 3000 3100 3400 2d00 3000 3900 2d00 3000 3700  :6.;.2.0.1.4.-.0.9.-.0.7.
    2000 3100 3300 3a00 3400 3100 3a00 3500 3500 3b00 3200 3000  : .1.3.:.4.1.:.5.5.;.2.0.
    3100 3400 2d00 3000 3900 2d00 3000 3700 2000 3100 3300 3a00  :1.4.-.0.9.-.0.7. .1.3.:.
    3400 3200 3a00 3200 3500 0d00 0a00 3100 3300 3600 3b00 3200  :4.2.:.2.5.....1.3.6.;.2.
    3000 3100 3400 2d00 3000 3900 2d00 3000 3700 2000 3100 3300  :0.1.4.-.0.9.-.0.7. .1.3.
    3a00 3400 3200 3a00 3200 3500 3b00 3200 3000 3100 3400 2d00  ::.4.2.:.2.5.;.2.0.1.4.-.
    3000 3900 2d00 3000 3700 2000 3100 3300 3a00 3400 3200 3a00  :0.9.-.0.7. .1.3.:.4.2.:.
    3500 3500 0d00 0a00 0000 0d00 0a00                           :5.5...........

Итак, как вы можете видеть, первые 2 байта - это xFF и xFE, и после каждого символа есть много x00. Концы строк представляют собой соединение 0D00 + 0A00, возврат каретки и перевод строки (\r\n) плюс x00.

Я хотел удалить эти x00 и первые 2 байта xFFxFE и последние 4 и преобразовать CRLF в LF.

Я мог бы сделать это, используя head, tail и tr:

    tr -d '\15\00' < 2014.log | tail -c +3 | head -c -2 > 3.log

Проблема в том, что я не уверен, что файл всегда будет поступать таким образом, поэтому мне нужно создать более общий метод. Я закончил с:

    sed 's/\xFF\xFE//g; s/\x00//g; s/\x0D//g' 2014.log > 2.log
    or
    tr -d '\377\376\00\15' < 2014.log > 2.log

Теперь мне нужно удалить последние две пустые строки, что, как я сказал в начале, должно быть легко, но я не могу этого сделать.

Я пробовал:

    sed '/^\s*$/d'
    sed '/^$/d'
    awk 'NF > 0'
    egrep -v "^$"
    Other stuff

Но в итоге удаляет только одну из пустых строк, у меня в итоге остается один x0A. Я попытался заменить соединение двух x0Ax0A на sed, даже используя \n\n, но это не сработало. Я не могу удалить все \n, потому что мне нужны обычные строки, я просто хочу удалить, когда они появляются как минимум два раза подряд. Опять же, я мог бы использовать хвост или голову, чтобы удалить его, но я бы предположил, что все файлы будут поступать таким образом, и это не так.

Я рассматриваю это как простой поиск и замену, но кажется, что это не работает, когда мы работаем с переводом строки.

В информационных целях:

    file -i 2014-09-07-13-46-51.log
    2014-09-07-13-46-51.log: application/octet-stream; charset=binary

Он не был распознан как текстовый файл... этот файл извлечен из общего объекта флэш-памяти (.sol).

Поскольку новые файлы могут быть не такими и пришли как обычные текстовые файлы, я не могу просто вырезать файлы, но мне нужно обработать проблемные.


person Luciano Serra    schedule 09.09.2014    source источник
comment
Это похоже на UTF-16 со спецификацией. Попробуйте открыть файл чем-то, что поддерживает эту кодировку. Затем посмотрите, сможете ли вы преобразовать его в лучшую кодировку для ваших целей.   -  person Etan Reisner    schedule 09.09.2014
comment
Я думаю, вы правы, это похоже на UTF-16 со спецификацией, я попытался сначала преобразовать его: iconv -f UTF-16 -t UTF-8, он удалил эти первые байты и 00, но последние байты запутался, возможно файл поврежден, 0d 0a00 0d0a   -  person Luciano Serra    schedule 09.09.2014
comment
В чем именно коррупция? Кажется, там есть случайный символ NUL, в котором я не уверен, что он может что-то сбить, я думаю. Воссоздание этого файла здесь, похоже, конвертируется правильно, но в последней строке есть случайный NUL байт.   -  person Etan Reisner    schedule 09.09.2014
comment
Да, между двумя последними CRLF стоит NUL (x00) - 0d0a 00 0d0a - с этим проблем нет, мне просто нужно удалить все это из файла, пустые строки плюс этот nul, и последний перевод строки   -  person Luciano Serra    schedule 09.09.2014
comment
Достаточно просто выполнить постобработку преобразованного файла, чтобы удалить этот набор из трех байтов с конца файла.   -  person Etan Reisner    schedule 09.09.2014
comment
Просто чтобы было понятнее, у меня есть тысячи файлов журналов, которые мне нужно импортировать, и я не могу их потерять, поэтому я не могу предположить, что все новые файлы будут одинаковыми, поэтому я пытаюсь создать метод который не будет менять файлы напрямую   -  person Luciano Serra    schedule 09.09.2014
comment
Я не видел ваш последний ответ, прежде чем я разместил свой. Проблема в том, что новые файлы журналов могут быть «исправленными», я имею в виду, с правильным кодированием и без конечных символов NUL или неправильных переводов строки (так должно быть с самого начала, но, к сожалению, у меня уже есть тысячи таких файлов для импорта) . Я не могу вырезать последние байты сразу. Дайте мне минуту, я постараюсь удалить их и дам вам обратную связь.   -  person Luciano Serra    schedule 09.09.2014
comment
Извините, моя репутация еще низкая, поэтому я должен опубликовать здесь. Как исходный вопрос, было бы легко, если бы я мог просто найти и заменить эти последние байты. Я попробовал еще раз с sed: iconv -f UTF-16 -t UTF-8 2014.log | sed 's/\x0d\x0a\x00\x0d\x0a//g' › 4.log - но опять же, это не работает с x0a - \n   -  person Luciano Serra    schedule 09.09.2014
comment
Я мог бы это сделать, но мне не понравилось решение... ну, вот оно: я конвертирую переводы строки в другой char с помощью tr, затем удаляю те, которые хочу (те, которые появляются более одного раза подряд), а затем конвертировать обратно: tr '\n' '|' | sed 's/||//g;' | sed 's/|/\x0A/g'   -  person Luciano Serra    schedule 09.09.2014
comment
sed работает на построчной основе, поэтому не может обрабатывать такие операции с новыми строками. Используйте инструмент, который может. awk с соответствующими настройками RS или ed или практически любого языка программирования.   -  person Etan Reisner    schedule 09.09.2014


Ответы (5)


«fffe» в начале файла — это знак порядка следования байтов (http://en.wikipedia.org/wiki/Byte_order_mark) и для меня указание на то, что у вас есть файл типа unicode. В таком файле «обычные» символы ascii представлены 2 байтами.

В другом вопросе/ответе stackoverflow файл сначала преобразуется в UTF-8... (grepping двоичные файлы и UTF16)

person Eddy    schedule 09.09.2014
comment
Спасибо за информацию о знаке порядка байтов! Как я сказал Итану в другом комментарии, я запускаю iconv для преобразования: iconv -f UTF-16 -t UTF-8, файл становится более «читаемым», хотя в некоторых редакторах он не открывается должным образом, потому что конечные байты получаются перепутал: 0d 0a00 0d0a - person Luciano Serra; 09.09.2014

Я, наконец, сделал это, но действительно не понравилось решение. Я заменил все переводы строки другим символом, например трубой (|), затем удалил, когда нашел два последовательно (||), а затем преобразовал трубы (|) обратно в \n

sed 's/\xFF\xFE//g; s/\x00//g; s/\x0D//g' 2014.log | tr '\n' '|' | sed 's/||//g;' | sed 's/|/\x0A/g' > 5.log

-- @Лучано

person Community    schedule 14.06.2016

Если вам просто нужны символы ASCII из файла, вы можете попробовать iconv

Вероятно, вы можете определить кодировку файла с помощью file -i

person dawg    schedule 09.09.2014
comment
файл -i 2014-09-07-13-46-51.log 2014-09-07-13-46-51.log: приложение/октет-поток; charset=binary Проблема в том, что эти файлы извлекаются из общего объекта флэш-памяти (.sol), и он уже поврежден. - person Luciano Serra; 09.09.2014

Я знаю, что вы просили sed, tr или awk, но на всякий случай это изменит ваше мнение, вот как легко заставить Perl делать тяжелую работу:

perl -e 'open my $fh, "<:encoding(utf16)", $ARGV[0] or die "Error reading $ARGV[0]: $!"; while (<$fh>) { s{\x0d\x0a}{\n}g; s{\x00\n}{}g; print $_; }' input_filename
person Tim    schedule 09.09.2014
comment
Завтра попробую и отпишусь! Возможно, я плохо выразился, мне не обязательно использовать эти 3 команды, мне просто нужно что-то, что делает работу в Debian. - person Luciano Serra; 10.09.2014

Вау, к тому времени я решил проблему, но забыл ответить, так что вот!

Используя только команду tr, я мог бы сделать это следующим образом:

tr -d '\377\376\015\000\277\003' ‹ logs.csv | тр -с '\n'

tr удалил все ненужные символы и пустые строки, и это было очень, очень быстро, намного быстрее, чем варианты с использованием sed и awk

person Luciano Serra    schedule 26.02.2019