Làm thế nào để nối Line với Line trước?


9

Tôi có một tệp Nhật ký cần được phân tích và phân tích. Tệp có chứa một cái gì đó tương tự như dưới đây:

Tập tin:

20141101 server contain dump
20141101 server contain nothing
    {uekdmsam ikdas 

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

Dựa trên kịch bản trên, tôi phải kiểm tra xem dòng bắt đầu không chứa ngày hay Số tôi phải nối vào dòng trước đó.

Tập tin đầu ra:

20141101 server contain dump
20141101 server contain nothing {uekdmsam ikdas jwdjamc ksadkek} ssfjddkc * kdlsdl sddsfd jfkdfk 
20141101 server contain dump

Câu trả lời:


11

Một phiên bản trong perl, sử dụng giao diện tiêu cực:

$ 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

-0cho phép regex được khớp trên toàn bộ tệp\n(?!([0-9]{8}|$))là giao diện phủ định, nghĩa là một dòng mới không được theo sau bởi 8 chữ số hoặc cuối dòng (trong đó, với -0, sẽ là phần cuối của tệp).


@terdon, được cập nhật để lưu dòng mới nhất.
muru

Đẹp quá Tôi sẽ ủng hộ bạn nhưng tôi sợ rằng tôi đã có :)
terdon

Không, -0nếu đối với các bản ghi phân định bằng NUL. Sử dụng -0777để nhét toàn bộ tệp vào bộ nhớ (mà bạn không cần ở đây).
Stéphane Chazelas

@ StéphaneChazelas Vậy cách nào tốt nhất để làm cho Perl khớp với dòng mới, ngoài việc đọc toàn bộ tệp trong?
muru

Xem các câu trả lời khác xử lý từng dòng tệp.
Stéphane Chazelas

5

Có thể dễ dàng một chút với sed

sed -e ':1 ; N ; $!b1' -e 's/\n\+\( *[^0-9]\)/\1/g'
  • phần đầu tiên :1;N;$!b1thu thập tất cả các dòng trong tệp chia cho \n1 dòng dài

  • phần thứ hai dải biểu tượng dòng mới nếu nó theo biểu tượng không có chữ số với khoảng trắng có thể có giữa nó.

Để tránh giới hạn bộ nhớ (đặc biệt cho các tệp lớn), bạn có thể sử dụng:

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

Hoặc quên một sedkịch bản khó và để nhớ rằng năm bắt đầu từ2

tr '\n2' ' \n' | sed -e '1!s/^/2/' -e 1{/^$/d} -e $a

Đẹp, +1. Bạn có thể thêm một lời giải thích về cách nó hoạt động không?
terdon

1
Ôi Đẹp. Tôi luôn tr '\n' $'\a' | sed $'s/\a\a*\( *[^0-9]\)/\1/g' | tr $'\a' '\n'tự làm .
mirabilos

Xin lỗi, phải downvote mặc dù đã sử dụng những thứ không phải là EXPRESSION EXULESS EXULESS EXICESSUL S trong sed (1) , đó là một GNU.
mirabilos

1
@Costas, đó là trang người đàn ông của GNU grep. Thông số kỹ thuật POSIX BRE đang . BRE tương đương với ERE +\{1,\}. [\n]cũng không phải là di động. \n\{1,\}sẽ là POSIX.
Stéphane Chazelas

1
Ngoài ra, bạn không thể có lệnh khác sau nhãn. : 1;xlà để xác định 1;xnhãn trong seds POSIX. Vì vậy, bạn cần : sed -e :1 -e 'N;$!b1' -e 's/\n\{1,\}\( *[^0-9]\)/\1/g'. Cũng lưu ý rằng nhiều sedtriển khai có giới hạn nhỏ về kích thước không gian mẫu của chúng (POSIX chỉ đảm bảo 10 x LINE_MAX IIRC).
Stéphane Chazelas

5

Một cách sẽ là:

 $ 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

Tuy nhiên, .that cũng loại bỏ dòng mới cuối cùng. Để thêm lại, sử dụng:

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

Giải trình

Các -lsẽ loại bỏ trailing dòng mới (và cũng có thể thêm một cho mỗi printcuộc gọi đó là lý do tôi sử dụng printfđể thay thế. Sau đó, nếu hiện tại bắt đầu phù hợp với con số ( /^\d+/) và số dòng hiện nay là lớn hơn một ( $.>1, điều này là cần thiết để tránh thêm một thêm dòng trống ở đầu), thêm a \nvào đầu dòng. printfIn mỗi dòng.


Ngoài ra, bạn có thể thay đổi tất cả các \nký tự thành \0, sau đó thay đổi các \0ký tự ngay trước một chuỗi số thành \nmột lần nữa:

$ 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

Để làm cho nó chỉ khớp với chuỗi 8 số, thay vào đó, hãy sử dụng chuỗi này:

$ tr '\n' '\0' < file | perl -pe 's/\0\d{8} |$/\n$&/g' | tr -d '\0'

Đối số đầu tiên printfđịnh dạng . Sử dụngprintf "%s", $_
Stéphane Chazelas

@ StéphaneChazelas tại sao? Ý tôi là, tôi biết nó sạch hơn và có lẽ dễ hiểu hơn nhưng có nguy hiểm nào bảo vệ khỏi nó không?
terdon

Có, nó sai và có khả năng nguy hiểm nếu đầu vào có thể chứa% ký tự. Hãy thử với một đầu vào với %10000000000sví dụ.
Stéphane Chazelas

Trong C, đó là một nguồn thực hành rất dễ nổi tiếng và rất dễ bị tổn thương. Với perl, echo %.10000000000f | perl -ne printfmang máy của tôi đến đầu gối của nó.
Stéphane Chazelas

@ StéphaneChazelas wow, vâng. Của tôi cũng thế. Đủ công bằng rồi, trả lời chỉnh sửa và cảm ơn.
terdon

3

Hãy thử làm điều này bằng cách sử dụng :

#!/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
}

Để dùng nó:

chmod +x script.awk
./script.awk file.txt

2

Một cách đơn giản nhất (hơn câu trả lời khác của tôi) bằng thuật toán terdon :

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

ITYM END{print ""}. Thay thế:awk -v ORS= 'NR>1 && /^[0-9]{8}/{print "\n"};1;END{print "\n"}'
Stéphane Chazelas


0

Chương trình Lê bash:

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

ở dạng một dòng:

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

Giải pháp với dấu gạch chéo ngược bảo tồn ( read -r) và khoảng trắng hàng đầu (ngay IFS=sau 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

mẫu một dòng:

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

Điều này sẽ phá vỡ nếu dòng chứa, giả sử, dấu gạch chéo ngược và dấu n. Nó cũng dải khoảng trắng. Nhưng bạn có thể sử dụng mkshđể làm điều này: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

Tất nhiên nó không dành cho mọi thứ thuật toán, mà là giải pháp cho các yêu cầu được cung cấp bởi tác vụ. Tất nhiên, giải pháp cuối cùng sẽ phức tạp hơn và ít đọc hơn trong nháy mắt vì nó thường xảy ra trong Real Life :)
rook

Tôi đồng ý, nhưng tôi đã học được một cách khó khăn để không giả định quá nhiều về OP ☺ đặc biệt nếu họ thay thế văn bản thực tế bằng văn bản giả.
mirabilos

0
[shyam@localhost ~]$ perl -lne 's/^/\n/ if $.>1 && /^\d+/; printf "%s",$_' appendDateText.txt

nó sẽ làm việc

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 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.