सीमांकक के साथ सूची को एकल पंक्ति में बदलें


17

मुझे इस प्रारूप में IP पतों की एक सूची (लोड) लेनी है:

 134.27.128.0
 111.245.48.0
 109.21.244.0

और उन्हें इस प्रारूप में एक पाइप के बीच में बदल दें (IPs बना हुआ)

134.27.128.0 | 111.245.48.0 | 109.21.244.0 | 103.22.200.0/22

मुझे लगता है कि यह एक खोज है और कमान की तरह है, sedलेकिन मैं इसे काम करने के लिए नहीं कर सकता।


3
तुम सिर्फ पाइप trमें newlines anslate करना चाहते हैं |? पसंद है <ipfile tr \\n \| >outfile?
मिकसरोव

क्या आसपास जगह की |आवश्यकता है?
cuonglm

2
@uslesslinuxman - नहीं। आपको इनपुट पुनर्निर्देशित करना होगा <। तो <mydoc tr \\n \| >mydoc2। लेकिन यह आपको रिक्त स्थान नहीं मिलेगा। उन लोगों के लिए, शायद सबसे तेज समाधान हैpaste -d' | ' mydoc /dev/null /dev/null >mydoc2
mikeserv

1
@ बाइक: मुझे नहीं लगता कि यह काम करेगा। pasteप्रत्येक फ़ाइल से संबंधित लाइनें लिखता है। इसके बिना -s, आपके पास फ़ाइल में मौजूद कई पंक्तियों की संख्या वापस आ जाएगी।
cuonglm

2
@ val0x00ff: मैं आपको unix.stackexchange.com/q/169716/38906
cuonglm

जवाबों:


16

का उपयोग करते हुए sed, के आधार पर प्रसिद्ध Sed एक-लाइनर समझाया, भाग I: : 39. संलग्न अगले अगर यह एक बैकस्लैश "\" के साथ समाप्त होता करने के लिए एक लाइन (यहाँ सिवाय हम बैकस्लैश के बारे में हिस्सा उपेक्षा, और की जगह \nके साथ नई-पंक्तियों आवश्यक |विभाजक):

sed -e :a -e '$!N; s/\n/ | /; ta' mydoc > mydoc2

में उत्पादन करना चाहिए mydoc2

134.27.128.0 |  111.245.48.0 |  109.21.244.0

@don_crissti खेद है कि एक प्रकार - सही किया गया था, धन्यवाद
स्टीलड्राइवर

यह वास्तव में अभ्यास में काम नहीं करता है, दुर्भाग्य से। कम से कम, असीमित धाराओं के लिए नहीं। जब आप ऐसा करते हैं तो आपको अपने पूरे इनपुट को एक बार एक पंक्ति में निगलना पड़ता है और आउटपुट तक इसकी एक भी बाइट नहीं लिख सकते हैं जब तक कि आप यह सब पचा नहीं लेते - यह सब एक ही लाइन में तब्दील हो जाता है। यह नाकाफी है और segfault का खतरा है।
mikeserv

एक मिलियन IP का <16M है, आपको यहां सीमाएं उड़ाने के लिए एक बड़ी सूची की आवश्यकता होगी। ईओफ़ खोज के लिए खोज का उपयोग करना अधिक समस्याग्रस्त है, क्योंकि यह इनपुट फ़ाइल आकार पर O (N ^ 2) चलाएगा। sed 'H;1h;$!d;x;s/\n/ | /g'रैखिक है।
21

@jthill - POSIX केवल sed8K के एक पैटर्न स्थान की गारंटी देता है ; यह 16M से बहुत कम है।
23 दिसंबर को रात

9

मैं यह देखने के लिए उत्सुक था कि इनमें से कुछ (+ कुछ विकल्प) एक बड़ी फ़ाइल के साथ गति-वार कैसे काम करते हैं ( 163MiB, IPप्रति पंक्ति, ~ 13 मिलियन लाइनें):

wc -l < iplist
13144256

परिणाम ( sync; echo 3 > /proc/sys/vm/drop_cachesप्रत्येक कमांड के बाद; मैंने परीक्षणों को दोहराया - रिवर्स ऑर्डर में - कुछ घंटों के बाद लेकिन मतभेद नगण्य थे; यह भी ध्यान दें कि मैं उपयोग कर रहा हूं gnu sed):

स्टीलड्राइवर :
बहुत धीमी गति से। दो मिनट के इंतजार के बाद गर्भपात हो गया ... इसलिए इसका कोई नतीजा नहीं निकला।

क्यूंग्लम :

awk 'FNR!=1{print l}{l=$0};END{ORS="";print l}' ORS=' | ' iplist

real    0m3.672s

perl -pe 's/\n/ | / unless eof' iplist

real    0m12.444s

mikeserv :

paste -d\  /dev/null iplist /dev/null | paste -sd\| - 

real    0m0.983s

जेथिल :

sed 'H;1h;$!d;x;s/\n/ | /g' iplist

real    0m4.903s

अविनाश राज :

time python2.7 -c'
import sys
with open(sys.argv[1]) as f:
    print " | ".join(line.strip() for line in f)' iplist

real    0m3.434s

तथा

val0x00ff :

while read -r ip; do printf '%s | ' "$ip"; done < iplist

real    3m4.321s

जिसका मतलब है 184.321sअप्रत्याशित रूप से, यह mikeserv के समाधान की तुलना में 200 गुना धीमा है ।


यहाँ कुछ और तरीके हैं
:

awk '$1=$1' RS= OFS=' | ' iplist

real    0m4.543s

awk '{printf "%s%s",sep,$0,sep=" | "} END {print ""}' iplist

real    0m5.511s

पर्ल:

perl -ple '$\=eof()?"\n":" | "' iplist

real    0m9.646s

xargs:

xargs <iplist printf ' | %s' | cut -c4-

real    0m6.326s

सिर + पेस्ट + tr + बिल्ली का एक संयोजन:

{ head -n -1 | paste -d' |' - /dev/null /dev/null | tr \\n \ ; cat ; } <iplist

real    0m0.991s

यदि आपके पास GNU coreutilsऔर यदि आपकी IP की सूची वास्तव में बहुत बड़ी नहीं है (तो आइए 50000 IP तक कहते हैं) आप इसके साथ भी ऐसा कर सकते हैं pr:

pr -$(wc -l infile) -tJS' | ' -W1000000 infile >outfile

कहाँ पे

-$(wc -l infile)         # no. of columns (= with no. of lines in your file)
-t                       # omit page headers and trailers
-J                       # merge lines
-S' | '                  # separate columns by STRING
-W1000000                # set page width

6-लाइनों वाली फ़ाइल के लिए उदाहरण:

134.28.128.0
111.245.28.0
109.245.24.0
128.27.88.0
122.245.48.0
103.44.204.0

आदेश:

pr -$(wc -l <infile) -tJS' | ' -W1000 infile

आउटपुट:

134.28.128.0 | 111.245.28.0 | 109.245.24.0 | 128.27.88.0 | 122.245.48.0 | 103.44.204.0

डॉन - आप while ... readलूप के लिए @ val0x00ff द्वारा प्रश्न में सुझाव भी जोड़ सकते हैं ? मैं यह देखने के लिए उत्सुक हूं कि 163k read()और write()कॉल क्या एक बेंचमार्क में अनुवाद करता है। शानदार जवाब, वैसे।
mikeserv

1
@mikeserv - कोई बात नहीं, मैं इसे करूँगा (यह वास्तव में धीमा होगा )।
17

यह एक बहुत अच्छा लिंक है। मुझे विशेष रूप से पसंद है कि लेखक समान 6 वर्षीय बेंचमार्क के लिए एक लिंक प्रदान करता है। क्या आपको sedलगता है कि उस समय में इसके खड़े होने में सुधार हुआ लगता है (और शायद इसके रेगेक्स इंजन में बहुत कम बदलाव हुए हैं) लेकिन grepलगता है कि इसके प्रदर्शन में नाटकीय रूप से गिरावट आई है (विशेषकर लंबी लाइनों के लिए) ? मुझे आश्चर्य है कि अगर perlइसके इंजन के अतिरिक्त उन परिणामों पर कोई असर पड़ता है ... यह भी साफ है कि dashयह निराशाजनक नहीं है । bashयहाँ की संभावना दूर धीमी w होगा / आम IFS=prepended।
mikeserv

हम्म ... वह लिंक अभी तक एक और मजबूत संकेतक है जिसे मुझे वास्तव में सीक करने और सी सीखने की आवश्यकता है इसलिए मैं अंत में lexठीक से उपयोग करना शुरू कर सकता हूं ।
mikeserv

8

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

awk 'FNR!=1{print l}{l=$0};END{ORS="";print l}' ORS=' | ' file > new_file

ORS=' | 'सेट उत्पादन रिकॉर्ड विभाजक के लिए ' | 'नई पंक्ति के बजाय।

या इसके साथ में संपादित करें perl:

perl -pe 's/\n/ | / unless eof' file

धन्यवाद दोस्त। मैंने सिर्फ सीखा कि कैसे pasteकाम करता है। बहुत सराहना की।
mikeserv

@ माइकर्स: आपका स्वागत है। जैसा कि उनके बेंचमार्क में don_crissti दिखाया गया है, pasteसमाधान सबसे तेज़ है।
cuonglm

आउटपुट एक नई रेखा के साथ समाप्त नहीं होता है। आपको ऐसा करने के साथ ब्लॉक के ORS=""अंदर बदलना पड़ सकता है। ENDORS="\n"
phk

4

इसलिए मैंने पूरी बात को गलत बताया - और इस सवाल ने मुझे बहुत कुछ सिखाया है paste। जब तक आप एरियल pasteमें एक फ़ाइल में नहीं होते हैं, तब तक कॉउन्ग्लम सही रूप से नोट करता है, आप -sहमेशा \nलिखा होगा कि आपकी शिशु सूची से w / अंतिम ईवलाइन को आउटपुट में जोड़ा जा रहा है। मुझे इस विश्वास में गलत समझा गया कि paste -sव्यवहार इसका डिफ़ॉल्ट मोड था - और यह एक गलत धारणा है, जो स्पष्ट रूप busybox pasteसे सुदृढ़ करने के लिए खुश थी। निम्नलिखित आदेश विज्ञापन w / के रूप में काम करता है busybox:

paste -d'|  ' - - infile </dev/null >outfile

यह कल्पना के अनुसार काम नहीं करता है, हालांकि। एक सही ढंग से लागू pasteकिया गया अभी भी \nप्रत्येक अनुक्रम के लिए एक अनुगामी ईवलाइन लिखा जाएगा। फिर भी, यह कोई बड़ी बात नहीं है:

paste -d\  - infile - </dev/null | paste -sd\| - >outfile

@don_crissti - ख़तरा। बेवकूफ गोली। मुझे लगता है कि करने के लिए स्पष्ट बात दो चिपकाता है।
मोइस्कर्व

1
ठीक है, मेरे prमन में था लेकिन जाहिरा तौर पर यह बड़ी इनपुट फाइलों के साथ भाप से बाहर निकलता है, इसलिए मैं वास्तव में गति का परीक्षण नहीं कर सका, लेकिन उचित लंबाई की फाइलों के साथ यह ठीक काम करता है। आप समाधान अब तक का सबसे तेज़ (कोई आश्चर्य नहीं - pasteवास्तव में तेज़ है), मेरी पोस्ट देखें।
don_crissti

4

एक-लाइनर के साथ tr और sed:

cat file | tr '\n' '|' | sed 's/||$/\n/'
134.27.128.0|111.245.48.0|109.21.244.0

2 अनुगामी पाइप क्यों हटाएं? यदि इनपुट एक रिक्त रेखा (दो newlines) के साथ समाप्त हो गया तो केवल 2 अंत में होगा।
जिगलीनागा

3

उपयोग vim:

vim -n -u NONE -c '1,$-1s/\n/ | /g|wq!' data

स्पष्टीकरण:

-n अक्षम स्वैप फ़ाइल

-u NONE का उपयोग सभी इनिशियलाइज़ेशन को छोड़ने के लिए किया जाता है।

-c {command} फ़ाइल पढ़ने के बाद कमांड निष्पादित करें।

1,$-1s/\n/ | /gहै s/\n/ | /gश्रृंखला के लिए (अंतरिक्ष पाइप की जगह के साथ न्यू लाइन की जगह) 1,$-1s(अंतिम पंक्ति के लिए 1 लाइन - 1)

wq! बल लिखना और छोड़ना


ध्यान दें:

आपकी फ़ाइल वास्तव में कितनी बड़ी है, इसके आधार पर, यह एक बुरा विचार हो सकता है।


1
मैं आप सभी को धन्यवाद देता हूं, क्योंकि मूल रूप से इनमें से लगभग हर एक कमांड मुझे जो हासिल करने की आवश्यकता है, उसके लिए काम करता है। मुझे पता है कि अब कहाँ आना है (अगर) मैं फिर से फंस गया हूँ। धन्यवाद
uselesslinuxman

2

अजगर के माध्यम से।

$ python -c '
import sys
with open(sys.argv[1]) as f:
    print " | ".join(line.strip() for line in f)' file

पहले स्थान printबहुत महत्वपूर्ण था।


2

यहाँ एक और एक का उपयोग कर रहा है xxd

xxd -c1 -ps data | sed '$!s/0a/207c20/' | xxd -r -ps

2

पूर्णता के लिए, यहां एक और- awkआधारित समाधान है, यह एक बिल्कुल भी उपयोग नहीं कर रहा है ORS:

awk 'BEGIN { ORS="" } { print p$0; p=" | " } END { print "\n" }' file > new_file

स्पष्टीकरण के लिए मेरी पोस्ट /unix//a/338121/117599 पर देखें

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.