Як додати рядок до попереднього рядка?


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
Ау. Приємно. Я завжди роблю tr '\n' $'\a' | sed $'s/\a\a*\( *[^0-9]\)/\1/g' | tr $'\a' '\n'себе.
mirabilos

Вибачте, але вам доведеться подати заявку, хоча за використання речей, які не є POSIX BASIC REGULAR EXPRESSION S в sed (1) , що є GNUism.
mirabilos

1
@Costas, це сторінка людини GNU grep. POSIX BRE специфікації є там . BRE еквівалент ERE +є \{1,\}. [\n]також не є портативним. \n\{1,\}буде POSIX.
Стефан Шазелас

1
Крім того, ви не можете мати іншої команди після мітки. : 1;xполягає у визначенні 1;xмітки в номерах POSIX. Так що вам потрібно: 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це потрібно, щоб уникнути додавання додаткового додаткового номера ) порожній рядок на початку), додайте a \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 вау, так. Мій також. Тоді справедливо, відповідь відредагована і дякую.
terdon

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

Ще один найпростіший спосіб (ніж моя інша відповідь) за допомогою і алгоритм Тердона :

awk 'NR>1 && /^[0-9]{8}/{printf "%s","\n"$0;next}{printf "%s",$0}END{print}' file

ІТМ END{print ""}. Альтернатива:awk -v ORS= 'NR>1 && /^[0-9]{8}/{print "\n"};1;END{print "\n"}'
Стефан Шазелас


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

Це порушиться, якщо рядок містить, скажімо, зворотний проріз та an 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

Звичайно, це не для всього алгоритму, а рішення для вимог, передбачених завданням. Звичайно, остаточне рішення буде більш складним і менш зрозумілим на перший погляд, як це зазвичай відбувається в реальному житті :)
грак

Я погоджуюсь, але я навчився важкого способу не припускати занадто багато про ОП ☺, особливо якщо вони замінюють фактичний текст фіктивним текстом.
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 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.