Как добавить строку в предыдущую строку?

9

У меня есть файл журнала, который нужно проанализировать и проанализировать. Файл содержит нечто подобное, как показано ниже:

Файл:

20141101 server contain dump
20141101 server contain nothing
    {uekdmsam ikdas 

jwdjamc ksadkek} ssfjddkc * kdlsdl
sddsfd jfkdfk 
20141101 server contain dump

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

Выходной файл:

20141101 server contain dump
20141101 server contain nothing {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdl sddsfd jfkdfk 
20141101 server contain dump
Уильям Р
источник

Ответы:

11

Версия perl, использующая негативные взгляды:

$ perl -0pe 's/\n(?!([0-9]{8}|$))//g' test.txt
20141101 server contain dump
20141101 server contain nothing    {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdlsddsfd jfkdfk
20141101 server contain dump

-0позволяет сопоставлять регулярное выражение по всему файлу и \n(?!([0-9]{8}|$))является отрицательным заглядыванием, означающим новую строку, за которой не следует 8 цифр, или конец строки (который, с -0, будет концом файла).

Мур
источник
@terdon, обновлено для сохранения последней новой строки.
Муру
Хороший! Я бы проголосовал за тебя, но, боюсь, уже успел :)
terdon
Нет, -0если для записей, разделенных NUL. Используйте, -0777чтобы выплеснуть весь файл в память (который вам здесь не нужен).
Стефан Шазелас
@ StéphaneChazelas Так каков лучший способ заставить Perl соответствовать символу новой строки, кроме как читать весь файл?
Муру
Смотрите другие ответы, которые обрабатывают файл построчно.
Стефан Шазелас
5

Может быть немного легко с sed

sed -e ':1 ; N ; $!b1' -e 's/\n\+\( *[^0-9]\)/\1/g'
  • первая часть :1;N;$!b1собрать все строки в файле, разделенные на \n1 длинную строку

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

Чтобы избежать ограничения памяти (особенно для больших файлов), вы можете использовать:

sed -e '1{h;d}' -e '1!{/^[0-9]/!{H;d};/^[0-9]/x;$G}' -e 's/\n\+\( *[^0-9]\)/\1/g'

Или забыть сложные sedсценарии и помнить, что год начинается с2

tr '\n2' ' \n' | sed -e '1!s/^/2/' -e 1{/^$/d} -e $a
Костас
источник
Ницца +1. Не могли бы вы добавить объяснение того, как это работает, пожалуйста?
Terdon
1
Aw. Ницца. Я всегда делаю tr '\n' $'\a' | sed $'s/\a\a*\( *[^0-9]\)/\1/g' | tr $'\a' '\n'сам.
Мирабилось
Извините, я должен понизить голос за использование вещей, которые не являются POSIX BASIC REGULAR EXPRESSION S в sed (1) , который является GNUism.
Мирабилось
1
@Costas, это справочная страница GNU grep. POSIX BRE спецификации есть . BRE эквивалент ERE +есть \{1,\}.[\n]тоже не портативно. \n\{1,\}будет POSIX.
Стефан Шазелас
1
Кроме того, у вас не может быть другой команды после метки. : 1;xявляется определение 1;xметки в POSIX seds. Так что вам нужно: sed -e :1 -e 'N;$!b1' -e 's/\n\{1,\}\( *[^0-9]\)/\1/g'. Также обратите внимание, что многиеsed реализации имеют небольшое ограничение на размер своего шаблонного пространства (POSIX гарантирует только 10 x LINE_MAX IIRC).
Стефан Шазелас
5

Одним из способов будет:

 $ perl -lne 's/^/\n/ if $.>1 && /^\d+/; printf "%s",$_' file
 20141101 server contain dump
 20141101 server contain nothing    {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdlsddsfd jfkdfk 
 20141101 server contain dump

Однако, это также удаляет заключительный перевод строки. Чтобы добавить его снова, используйте:

$ { perl -lne 's/^/\n/ if $.>1 && /^\d+/; printf "%s",$_' file; echo; } > new

объяснение

Он -lудалит завершающие символы новой строки (а также добавит одну к каждому printвызову, поэтому я использую printfвместо этого. Затем, если текущая строка начинается с цифр ( /^\d+/), а номер текущей строки больше единицы ( $.>1это необходимо, чтобы избежать добавления дополнительных пустая строка в начале), добавьте \nв начало строки printf.Печатает каждую строку.


Кроме того, вы можете изменить все \nсимволы на \0, а затем изменить те, \0которые находятся прямо перед строкой чисел, \nснова:

$ tr '\n' '\0' < file | perl -pe 's/\0\d+ |$/\n$&/g' | tr -d '\0'
20141101 server contain dump
20141101 server contain nothing    {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdlsddsfd jfkdfk 
20141101 server contain dump

Чтобы он соответствовал только строкам из 8 чисел, используйте вместо этого:

$ tr '\n' '\0' < file | perl -pe 's/\0\d{8} |$/\n$&/g' | tr -d '\0'
Тердон
источник
Первый аргумент в printfэто формат . Использованиеprintf "%s", $_
Стефан Шазелас
@ StéphaneChazelas почему? Я имею в виду, я знаю, что это чище и, возможно, легче понять, но есть ли опасность, от которой это защитит?
Тердон
Да, это неправильно и потенциально опасно, если ввод может содержать% символов. Попробуйте с вводом, %10000000000sнапример.
Стефан Шазелас
В Си это очень известный очень плохой источник и источник уязвимости. С того perl, echo %.10000000000f | perl -ne printfставит мою машину на колени.
Стефан Шазелас
@ StéphaneChazelas Ого, да. Мой тоже. Достаточно справедливо, ответ отредактирован и спасибо.
Тердон
3

Попробуйте сделать это с помощью :

#!/usr/bin/awk -f

{
    # if the current line begins with 8 digits followed by
    # 'nothing' OR the current line doesn't start with 8 digits
    if (/^[0-9]{8}.*nothing/ || !/^[0-9]{8}/) {
        # print current line without newline
        printf "%s", $0
        # feeding a 'state' variable
        weird=1
    }
    else {
        # if last line was treated in the 'if' statement
        if (weird==1) {
            printf "\n%s", $0
            weird=0
        }
        else {
            print # print the current line
        }
    }
}
END{
    print # add a newline when there's no more line to treat
}

Чтобы использовать это:

chmod +x script.awk
./script.awk file.txt
Жиль Квено
источник
2

Другой простой способ (чем мой другой ответ) с использованием алгоритма и terdon :

awk 'NR>1 && /^[0-9]{8}/{printf "%s","\n"$0;next}{printf "%s",$0}END{print}' file
Жиль Квено
источник
ITYM END{print ""}. Альтернатива:awk -v ORS= 'NR>1 && /^[0-9]{8}/{print "\n"};1;END{print "\n"}'
Стефан Шазелас
1
sed -e:t -e '$!N;/\n *[0-9]{6}/!s/\n */ /;tt' -eP\;D
mikeserv
источник
0

Le program en bash:

while read LINE
do
    if [[ $LINE =~ ^[0-9]{8} ]]
    then
        echo -ne "\n${LINE} "
    else
        echo -n "${LINE} "
    fi
done < file.txt

в однострочном виде:

while read L; do if [[ $L =~ ^[0-9]{8} ]]; then echo -ne "\n${L} "; else echo -n "${L} "; fi done < file.txt

Решение с сохранением обратной косой черты ( read -r) и пробелами (сразу IFS=после while):

while IFS= read -r LINE
do
    if [[ $LINE =~ ^[0-9]{8} ]]
    then
        echo
        echo -nE "\n${LINE} "
    else
        echo -nE "${LINE} "
    fi
done < file.txt

однострочная форма:

while IFS= read -r L; do if [[ $L =~ ^[0-9]{8} ]]; then echo; echo -nE "${L} "; else echo -nE "${L} "; fi done < file.text
ладья
источник
Это сломается, если строка содержит, скажем, обратную косую черту и n. Это также удаляет пробелы. Но вы можете использовать mkshдля этого:while IFS= read -r L; do [[ $L = [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]* ]] && print; print -nr -- "$L"; done; print
mirabilos
Конечно, это не для всего алгоритма, а для решения требований, поставленных задачей. Конечно, окончательное решение будет более сложным и менее читабельным, как это обычно бывает в реальной жизни :)
ладья
Я согласен, но я выучил сложный способ не слишком много думать о ФП, особенно если они заменяют фактический текст фиктивным.
Мирабилось
0
[shyam@localhost ~]$ perl -lne 's/^/\n/ if $.>1 && /^\d+/; printf "%s",$_' appendDateText.txt

Это будет работать

i/p:
##06/12/2016 20:30 Test Test Test
##TestTest
##06/12/2019 20:30 abbs  abcbcb abcbc
##06/11/2016 20:30 test test
##i123312331233123312331233123312331233123312331233Test
## 06/12/2016 20:30 abc

o/p:
##06/12/2016 20:30 Test Test TestTestTest
##06/12/2019 20:30 abbs  abcbcb abcbc
##06/11/2016 20:30 test ##testi123312331233123312331233123312331233123312331233Test
06/12/2016 20:30 abc vi appendDateText.txt 
Шьям Гупта
источник