पिछले लाइन के लिए लाइन कैसे संलग्न करें?


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यदि एनयूएल-सीमांकित रिकॉर्ड के लिए। -0777मेमोरी में संपूर्ण फ़ाइल को खुरचने के लिए उपयोग करें (जिसे आपको यहां करने की आवश्यकता नहीं है)।
स्टीफन चेज़लस

@ स्टीफनचेज़लस तो पर्ल को न्यूलाइन बनाने का सबसे अच्छा तरीका है, पूरी फाइल पढ़ने के अलावा?
मुरु

अन्य उत्तरों को देखें जो फ़ाइल लाइन को लाइन द्वारा संसाधित करते हैं।
स्टीफन चेजेलस

5

थोड़ा आसान हो सकता है sed

sed -e ':1 ; N ; $!b1' -e 's/\n\+\( *[^0-9]\)/\1/g'
  • पहला भाग 1 लंबी लाइन में :1;N;$!b1विभाजित फ़ाइल में सभी लाइनों को इकट्ठा करता है\n

  • यदि इसके बीच संभव रिक्त स्थान के साथ गैर-अंकों के प्रतीक का पालन किया गया तो दूसरा भाग पट्टी न्यूलाइन प्रतीक।

स्मृति सीमा से बचने के लिए (बड़ी फ़ाइलों के लिए espesially) आप उपयोग कर सकते हैं:

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

अच्छा लगा, १। क्या आप इस बात की व्याख्या जोड़ सकते हैं कि यह कैसे काम करता है?
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
@ कोस्टस, वह ग्नू ग्रीप का मैन पेज है। POSIX BRE कल्पना कर रहे हैं वहाँ । ERE +का BRE समकक्ष है \{1,\}[\n]पोर्टेबल भी नहीं है। \n\{1,\}POSIX होगा।
स्टीफन चेज़लस

1
इसके अलावा, लेबल के बाद आपके पास दूसरा कमांड नहीं हो सकता है। POSIX seds में लेबल : 1;xको परिभाषित करना है 1;x। तो आप की जरूरत है 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

हालाँकि, यह भी अंतिम newline को निकालता है। इसे फिर से जोड़ने के लिए, उपयोग करें:

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

व्याख्या

-lनई-पंक्तियों अनुगामी (और यह भी प्रत्येक के लिए एक जोड़ने निकाल देंगे printकॉल है, जिसके कारण मैं का उपयोग printfकरने के बजाय। तो फिर, अगर संख्या (के साथ मौजूदा लाइन शुरू होता है /^\d+/) और वर्तमान लाइन संख्या एक से अधिक (है $.>1, इस एक अतिरिक्त जोड़ने से बचने के लिए की जरूरत है शुरुआत में खाली लाइन), लाइन की शुरुआत में एक जोड़ें \nprintfप्रत्येक लाइन को प्रिंट करता है।


वैकल्पिक रूप से, आप सभी \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", $_
स्टीफन चेज़लस

@ स्टीफनचेज़ेलस क्यों? मेरा मतलब है, मुझे पता है कि यह क्लीनर है और शायद यह समझना आसान है लेकिन क्या कोई खतरा है जो इससे बचाएगा?
terdon

हां, यह गलत और संभावित खतरनाक है यदि इनपुट में% वर्ण हो सकते हैं। %10000000000sउदाहरण के लिए इनपुट के साथ प्रयास करें ।
स्टीफन चेज़लस

सी में, यह एक बहुत अच्छी तरह से जाना जाता है बहुत बुरा अभ्यास और भेद्यता स्रोत है। के साथ perl, echo %.10000000000f | perl -ne printfमेरी मशीन को अपने घुटनों पर लाता है।
स्टीफन चेज़लस

@ स्टीफनचेज़ेलस वाह, हाँ। मेरा भि। तब निष्पक्ष रूप से, उत्तर संपादित और धन्यवाद।
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

ITYM END{print ""}। वैकल्पिक:awk -v ORS= 'NR>1 && /^[0-9]{8}/{print "\n"};1;END{print "\n"}'
स्टीफन चेज़लस


0

ले प्रोग्राम एन बैश:

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

बेशक यह सब कुछ एल्गोरिथ्म के लिए नहीं है, लेकिन कार्य द्वारा प्रदान की गई आवश्यकताओं के लिए समाधान है। बेशक अंतिम समाधान एक नज़र में अधिक जटिल और कम पठनीय होगा क्योंकि यह आम तौर पर रियल लाइफ में होता है :)
किश्ती

मैं सहमत हूं, लेकिन मैंने ओपी OP के बारे में बहुत अधिक नहीं मानने का कठिन तरीका सीखा है, खासकर यदि वे वास्तविक पाठ को डमी टेक्स्ट द्वारा प्रतिस्थापित करते हैं।
मिराबिलोस

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 Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.