कमांड लाइन से हर दो लाइनों को एक में कैसे मर्ज करें?


151

मेरे पास निम्नलिखित प्रारूप के साथ एक पाठ फ़ाइल है। पहली पंक्ति "कुंजी" है और दूसरी पंक्ति "VALUE" है।

KEY 4048:1736 string
3
KEY 0:1772 string
1
KEY 4192:1349 string
1
KEY 7329:2407 string
2
KEY 0:1774 string
1

मुझे कुंजी के रूप में उसी पंक्ति में मान की आवश्यकता है। तो आउटपुट इस तरह दिखना चाहिए ...

KEY 4048:1736 string 3
KEY 0:1772 string 1
KEY 4192:1349 string 1
KEY 7329:2407 string 2
KEY 0:1774 string 1

यह बेहतर होगा अगर मैं कुछ सीमांकक का उपयोग कर सकता हूं जैसे $या ,:

KEY 4048:1736 string , 3

मैं दो लाइनों को एक में कैसे मिलाऊँ?


ऐसा करने का एक बहुत तरीका है! मैं एक किया है के साथ थोड़ा बेंच pr, paste, awk, xargs, sedऔरpure bash ! ( बैशxargs की तुलना में धीमा, धीमा है !)
एफ। होरी

जवाबों:


182

awk:

awk 'NR%2{printf "%s ",$0;next;}1' yourFile

ध्यान दें, आउटपुट के अंत में एक खाली लाइन है।

sed:

sed 'N;s/\n/ /' yourFile

रंगीन आउटपुट के साथ काम नहीं करता है। मैं इस क्यू एंड ए पर सब कुछ करने की कोशिश की और कुछ भी काम नहीं किया जब उत्पादन एएनआई रंग का है। उबंटू 13.04 पर परीक्षण किया गया
लियो गैलुकि

1
@elgalu: क्योंकि एएनएसआई रंग केवल भागने चरित्र संयोजनों का एक गुच्छा है। इस तरह के आउटपुट पर हेक्सिडिट करें, यह देखने के लिए कि आपके पास क्या है।
19

7
यदि इस printfतरह के विस्तार तार %sभीतर पाए जाते हैं तो यह अजीब समाधान टूट सकता है $0। इस तरह की विफलता से बचा जा सकता है:'NR%2{printf "%s ",$0;next;}1'
ghoti

9
क्योंकि यह वास्तव में Google के लिए कठिन है, 1समापन ब्रेस के बाद क्या मतलब है?
erikbwork


243

paste इस काम के लिए अच्छा है:

paste -d " "  - - < filename

10
मुझे लगता है कि यह सबसे अच्छा समाधान प्रस्तुत किया गया है, न तो सेड और न ही awk का उपयोग करने के बावजूद। इनपुट पर जो लाइनों की एक विषम संख्या है, केंट का awk समाधान अंतिम नई रेखा को छोड़ देता है, उसका sed समाधान अपने अंतिम भाग में अंतिम रेखा को छोड़ देता है, और मेरा समाधान अंतिम पंक्ति को दोहराता है। pasteदूसरी ओर, पूरी तरह से व्यवहार करता है। +1।
घोटी

8
मैं अक्सर उपयोग करता हूं cutलेकिन हमेशा भूल जाता हूं paste। यह इस समस्या के लिए चट्टानों। मुझे स्टड से सभी लाइनों को संयोजित करने की आवश्यकता थी और इसे आसानी से किया paste -sd ' ' -
क्लिंट पाच

4
सरल और सुंदर!
krlmlr

8
तो -स्टड paste - -से मतलब है, तो स्टाइन से पढ़ें, तो स्टड से पढ़ें, आप उनमें से कई को स्टैक कर सकते हैं जैसा कि आप चाहते हैं कि मैं उम्मीद करता हूं।
थोरसुमोनर

1
हां, @ थोरसुमोनर ... मुझे हर तीन लाइनों को एक ही लाइन में पेस्ट करना था और पेस्ट करना था - - - और यह पूरी तरह से काम करता है।
डैनियल गोल्डफर्ब

35

Sed, awk, grep के लिए वैकल्पिक:

xargs -n2 -d'\n'

यह सबसे अच्छा है जब आप एन लाइनों में शामिल होना चाहते हैं और आपको केवल स्पेस सीमांकित आउटपुट की आवश्यकता है।

मेरा मूल उत्तर था xargs -n2जो लाइनों के बजाय शब्दों पर अलग था । -dकिसी भी एकल वर्ण द्वारा इनपुट को विभाजित करने के लिए इस्तेमाल किया जा सकता है।


4
यह एक अच्छा तरीका है, लेकिन यह शब्दों पर काम करता है, न कि लाइनों पर। इसे लाइनों पर काम करने के लिए, जोड़ा जा सकता है-d '\n'
डॉन हैच

2
वाह, मैं एक नियमित xargsउपयोगकर्ता हूं लेकिन यह नहीं जानता था। महान टिप।
श्रीधर सरनोबत

1
मुझे यह पसंद है। इतना साफ।
अलेक्जेंडर गुओ

28

कुत्ते को मारने के बजाय मारने के और तरीके हैं। [1]

awk '{key=$0; getline; print key ", " $0;}'

उद्धरणों के अंदर आपको जो भी परिसीमन करना है, उसे डालें।


संदर्भ:

  1. मूल रूप से "बिल्ली की त्वचा के लिए बहुत सारे तरीके", एक पुरानी, ​​संभावित मूल अभिव्यक्ति के लिए वापस आ गया है, जिसका पालतू जानवरों से कोई लेना-देना नहीं है।

मुझे यह समाधान पसंद है।
luis.espinal

5
एक बिल्ली के मालिक के रूप में मैं इस तरह के हास्य की सराहना नहीं करता हूं।
witkacy26

4
@ witkacy26, आपकी चिंता के अनुसार समायोजित अभिव्यक्ति।
घटि

मैं इस अजीब समाधान से प्यार करता हूँ, लेकिन मुझे समझ नहीं आता कि यह कैसे काम करता है: S
रुबेंडोब

@Rubendob - awk इनपुट की प्रत्येक पंक्ति को पढ़ता है, और इसे चर में रखता है $0getlineआदेश भी पकड़ लेता है इनपुट और यह स्थानों में से "अगला" लाइन $0। तो पहला स्टेटमेंट पहली लाइन को पकड़ लेता है, और प्रिंट कमांड कॉनैट keyको एक स्ट्रिंग के साथ वेरिएबल में सेव किया गया था , साथ ही उस लाइन के साथ, जिसे प्रयोग करके लाया गया था getline। स्पष्ट? :)
गोटी

12

यहाँ मेरा समाधान में है:

while read line1; do read line2; echo "$line1, $line2"; done < data.txt

11

हालांकि ऐसा लगता है कि पिछले समाधान काम करेंगे, अगर दस्तावेज़ में एक एकल विसंगति होती है तो आउटपुट टुकड़ों में जाएगा। नीचे थोड़ा सा सुरक्षित है।

sed -n '/KEY/{
N
s/\n/ /p
}' somefile.txt

3
क्यों सुरक्षित है? क्या करता /KEY/है? आखिर में क्या करता है p?
स्टीवर्ट

के /KEY/साथ लाइन के लिए खोजों KEYpपरिणाम को प्रिंट करता है। यह सुरक्षित है क्योंकि यह केवल उसी में लाइनों के साथ ऑपरेशन को लागू करता KEYहै।
मिंगहुआ

11

यहाँ एक और तरीका है awk:

awk 'ORS=NR%2?FS:RS' file

$ cat file
KEY 4048:1736 string
3
KEY 0:1772 string
1
KEY 4192:1349 string
1
KEY 7329:2407 string
2
KEY 0:1774 string
1

$ awk 'ORS=NR%2?FS:RS' file
KEY 4048:1736 string 3
KEY 0:1772 string 1
KEY 4192:1349 string 1
KEY 7329:2407 string 2
KEY 0:1774 string 1

जैसा कि टिप्पणियों में एड मॉर्टन ने संकेत दिया है , पोर्टेबिलिटी के लिए सुरक्षा और पार्न्स के लिए ब्रेसिज़ जोड़ना बेहतर है।

awk '{ ORS = (NR%2 ? FS : RS) } 1' file

ORSआउटपुट रिकॉर्ड सेपरेटर के लिए खड़ा है। हम यहां जो कर रहे हैं वह एक शर्त का परीक्षण कर रहा है NRजो लाइन नंबर को संग्रहीत करता है। यदि का modulo NRएक सही मान है (> 0) तो हम आउटपुट फील्ड सेपरेटर को FS(फील्ड सेपरेटर) के मान पर सेट करते हैं, जो डिफ़ॉल्ट रूप से स्पेस है, अन्यथा हम RS(रिकॉर्ड सेपरेटर) के मान को असाइन करते हैं जो कि न्यूलाइन है।

यदि आप ,विभाजक के रूप में जोड़ना चाहते हैं तो निम्नलिखित का उपयोग करें:

awk '{ ORS = (NR%2 ? "," : RS) } 1' file

1
निश्चित रूप से सही दृष्टिकोण तो +1 लेकिन मुझे आश्चर्य है कि रिकॉर्ड को प्रिंट करने की डिफ़ॉल्ट कार्रवाई को लागू करने के लिए किस स्थिति का मूल्यांकन किया जा रहा है। क्या यह है कि असाइनमेंट सफल हुआ? क्या यह सरल है ORSऔर इसका इलाज trueतब से किया जा रहा है जब तक कि ओआरएस का मूल्य शून्य या शून्य स्ट्रिंग नहीं हो जाता है और सही ढंग से अनुमान लगाता है कि यह संख्यात्मक तुलना के बजाय एक स्टिंग होना चाहिए? यह कुछ और है? मुझे यकीन नहीं है और इसलिए मैंने इसे लिखा होगा awk '{ORS=(NR%2?FS:RS)}1' file। मैंने पोर्टेबिलिटी सुनिश्चित करने के लिए टर्नरी एक्सप्रेशन को छोटा किया।
एड मॉर्टन

1
@EdMorton हाँ, मैंने सिर्फ इस उत्तर पर युगल को देखा था जो सुरक्षा के लिए ब्रेसिज़ को शामिल करने के लिए इसे अपडेट करने वाला था। के रूप में अच्छी तरह से parens जोड़ देगा।
जयपाल सिंह

7

"पूर्व" एक स्क्रिप्ट योग्य लाइन संपादक है जो उसी परिवार में है जैसे कि sed, awk, grep, आदि। मुझे लगता है कि यह वही हो सकता है जिसे आप खोज रहे हैं। कई आधुनिक vi क्लोन / उत्तराधिकारियों में भी vi मोड है।

 ex -c "%g/KEY/j" -c "wq" data.txt

यह प्रत्येक पंक्ति के लिए कहता है, यदि यह "कुंजी" से मेल खाता है, तो निम्नलिखित पंक्ति का एक j oin करें। उसके बाद कमांड पूरी होती है (सभी लाइनों के खिलाफ), एक w rite और q uit जारी करें।


4

यदि पर्ल एक विकल्प है, तो आप कोशिश कर सकते हैं:

perl -0pe 's/(.*)\n(.*)\n/$1 $2\n/g' file.txt

करता -0बताई पर्ल रिकॉर्ड विभाजक (स्थापित करने के लिए $/)अशक्त करने के लिए, ताकि हम अपने मिलान पैटर्न में कई पंक्तियों अवधि सकते हैं manpages कुछ अधिक तकनीकी मुझे यह पता लगाने की क्या यह व्यवहार में इसका मतलब
श्रीधर सरनोबत

4

आप इस तरह का उपयोग कर सकते हैं कभी लाइनों के 2 जोड़ी गठबंधन करने के लिए:

awk '{ if (NR%2 != 0) line=$0; else {printf("%s %s\n", line, $0); line="";} } \
     END {if (length(line)) print line;}' flle

4

विम (केवल संदर्भ के लिए) का उपयोग कर एक और समाधान।

समाधान 1 :

Vim में फ़ाइल खोलें vim filename, फिर कमांड निष्पादित करें:% normal Jj

इस आदेश को समझना बहुत आसान है:

  • %: सभी लाइनों के लिए,
  • सामान्य: सामान्य कमांड निष्पादित करें
  • जज: कमांड ज्वाइन करें, फिर नीचे की लाइन पर जाएं

उसके बाद, फ़ाइल को सहेजें और साथ बाहर निकलें :wq

समाधान 2 :

शेल में कमांड निष्पादित करें vim -c ":% normal Jj" filename, फिर फ़ाइल को सहेजें और साथ बाहर निकलें :wq


मामले में norm!और भी मजबूत । विम समाधान के लिए +1। normalJ
11

@qeatzy मुझे यह सिखाने के लिए धन्यवाद। यह जानकर बहुत खुशी हुई। ^ _ ^
जेन्सन

3

आप निम्न vi आदेश का भी उपयोग कर सकते हैं:

:%g/.*/j

या फिर भी :%g//jजब से आप सभी की जरूरत है, शामिल होने के लिए एक मैच निष्पादित किया जाता है, और एक अशक्त स्ट्रिंग अभी भी एक वैध रेगेक्स है।
घटि

1
@ghoti, In Vim, बस का उपयोग करते समय //, पिछले खोज पैटर्न का उपयोग किया जाएगा। यदि कोई पिछला पैटर्न नहीं है, तो विम केवल एक त्रुटि की रिपोर्ट करता है और कुछ भी नहीं करता है। Jdamian का समाधान हर समय काम करता है।
16:54 पर डेविड वॉन्ग

1
@TzunghsingDavidWong - यह विम उपयोगकर्ताओं के लिए एक अच्छा सूचक है। मेरे लिए हाथ से, न तो प्रश्न और न ही इस उत्तर ने विम का उल्लेख किया।
घोटी

3

ग्लेन जैकमैन के उत्तर पर एक मामूली बदलाव का उपयोग करते हुए paste: यदि -dसीमांकक विकल्प के लिए मान में एक से अधिक वर्ण हैं, तो एक- pasteएक करके वर्णों के माध्यम से चक्र, और -sविकल्पों के साथ संयुक्त एक ही इनपुट फ़ाइल को संसाधित करते समय ऐसा करता रहता है।

इसका मतलब यह है कि हम विभाजक के रूप में जो कुछ भी चाहते हैं उसका उपयोग कर सकते हैं \nऔर एक समय में दो लाइनों को मर्ज करने के लिए भागने का क्रम ।

अल्पविराम का उपयोग करना:

$ paste -s -d ',\n' infile
KEY 4048:1736 string,3
KEY 0:1772 string,1
KEY 4192:1349 string,1
KEY 7329:2407 string,2
KEY 0:1774 string,1

और डॉलर का चिह्न:

$ paste -s -d '$\n' infile
KEY 4048:1736 string$3
KEY 0:1772 string$1
KEY 4192:1349 string$1
KEY 7329:2407 string$2
KEY 0:1774 string$1

यह जो नहीं कर सकता है वह एक विभाजक का उपयोग करता है जिसमें कई वर्ण हैं।

एक बोनस के रूप में, यदि pastePOSIX का अनुपालन होता है, तो यह फ़ाइल में अंतिम पंक्ति की नई रेखा को संशोधित नहीं करेगा, इसलिए एक इनपुट फ़ाइल के लिए विषम संख्या में लाइनों की तरह

KEY 4048:1736 string
3
KEY 0:1772 string

paste अंतिम पंक्ति पर जुदाई के चरित्र से निपटने नहीं होगा:

$ paste -s -d ',\n' infile
KEY 4048:1736 string,3
KEY 0:1772 string

1
nawk '$0 ~ /string$/ {printf "%s ",$0; getline; printf "%s\n", $0}' filename

यह पढ़ता है

$0 ~ /string$/  ## matches any lines that end with the word string
printf          ## so print the first line without newline
getline         ## get the next line
printf "%s\n"   ## print the whole line and carriage return

1

उस मामले में जहां मुझे दो लाइनों (आसान प्रसंस्करण के लिए) को संयोजित करने की आवश्यकता थी, लेकिन डेटा को विशिष्ट के अतीत की अनुमति दें, मैंने इसे उपयोगी पाया

data.txt

string1=x
string2=y
string3
string4
cat data.txt | nawk '$0 ~ /string1=/ { printf "%s ", $0; getline; printf "%s\n", $0; getline } { print }' > converted_data.txt

आउटपुट तब दिखता है:

converted_data.txt

string1=x string2=y
string3
string4

1

विम का उपयोग करने वाला एक और तरीका होगा:

:g/KEY/join

यह उन joinसभी रेखाओं पर लागू होता है (जिसमें इसके नीचे की रेखा होती है) इसमें सभी शब्द होते KEYहैं। परिणाम:

KEY 4048:1736 string 3
KEY 0:1772 string 1
KEY 4192:1349 string 1
KEY 7329:2407 string 2
KEY 0:1774 string 1

0

सबसे सरल तरीका यहाँ है:

  1. यहां तक ​​कि लाइनें निकालें और इसे कुछ अस्थायी फ़ाइल 1 में लिखें।
  2. विषम पंक्तियों को हटा दें और इसे कुछ अस्थायी फ़ाइल 2 में लिखें।
  3. -D के साथ पेस्ट कमांड का उपयोग करके एक में दो फाइलों को मिलाएं (मतलब स्थान हटाएं)

sed '0~2d' file > 1 && sed '1~2d' file > 2 && paste -d " " 1 2

0
perl -0pE 's{^KEY.*?\K\s+(\d+)$}{ $1}msg;' data.txt > data_merged-lines.txt

-0इसे लाइन-बाय-लाइन पढ़ने के बजाय पूरी फ़ाइल को gobbles;
pEलूप के साथ कोड लपेटता है और आउटपुट प्रिंट करता है, http://perldoc.perl.org/perlrun.html में विवरण देखें ;
^KEYपंक्ति की शुरुआत में "कुंजी", .*?अनुक्रम के पहले कुछ भी ( ) के गैर-लालची मैच के बाद

  1. \s+लाइन ब्रेक सहित किसी भी प्रकार का एक या अधिक स्थान ;
  2. एक या अधिक अंक (\d+)जिसे हम कैप्चर करते हैं और बाद में फिर से सम्मिलित करते हैं $1;

पंक्ति के अंत के बाद $

\Kप्रतिस्थापन से सब कुछ आसानी से बाहर कर { $1}देता है, इसलिए केवल 1-2 अनुक्रम को प्रतिस्थापित करता है , http://perldoc.perl.org/permre.html देखें ।


0

शेल स्क्रिप्ट के रूप में एक अधिक-सामान्य समाधान (एक से अधिक अनुवर्ती पंक्ति में शामिल होने की अनुमति देता है)। यह प्रत्येक के बीच एक रेखा जोड़ता है, क्योंकि मुझे दृश्यता की आवश्यकता थी, लेकिन यह आसानी से ठीक हो जाता है। यह उदाहरण है, जहां "की" लाइन समाप्त हो गई: और अन्य लाइनों ने नहीं किया।

#!/bin/bash
#
# join "The rest of the story" when the first line of each   story
# matches $PATTERN
# Nice for looking for specific changes in bart output
#

PATTERN='*:';
LINEOUT=""
while read line; do
    case $line in
        $PATTERN)
                echo ""
                echo $LINEOUT
                LINEOUT="$line"
                        ;;
        "")
                LINEOUT=""
                echo ""
                ;;

        *)      LINEOUT="$LINEOUT $line"
                ;;
    esac        
done

-1

निम्नलिखित पंक्ति आज़माएँ:

while read line1; do read line2; echo "$line1 $line2"; done <old.txt>new_file

बीच-बीच में सीमांकक डालें

"$line1 $line2";

जैसे अगर सीमांकक है |, तो:

"$line1|$line2";

यह उत्तर आपके द्वारा 4 साल पहले पोस्ट की गई Hai Vu के उत्तर में प्रदान की गई कुछ भी नहीं जोड़ रहा है ।
फेडोरक्वी 'एसओ ने'

मैं आंशिक रूप से सहमत हूं, मैं स्पष्टीकरण और अधिक सामान्य जोड़ने की कोशिश करता हूं यह पुरानी फ़ाइल को भी संपादित नहीं करेगा। आपके सुझाव के लिए धन्यवाद
सुमन

-2

आप xargsइस तरह का उपयोग कर सकते हैं :

xargs -a file

% cat> फ़ाइल abc% xargs -a फ़ाइल abc% मेरे लिए काम करता है
RSG

यह कुछ करता है, हाँ, लेकिन ओपी ने जो नहीं मांगा। विशेष रूप से, यह संभव के रूप में कई लाइनों में मिलती है। आप वास्तव में वही प्राप्त कर सकते हैं जो आप चाहते हैं, xargs -n 2लेकिन यह उत्तर इस बात की व्याख्या नहीं करता है।
ट्रिपलए
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.