เปลี่ยนรายการเป็นบรรทัดเดียวด้วยตัวคั่น


17

ฉันต้องจดรายการ (โหลด) ที่อยู่ IP ในรูปแบบนี้:

 134.27.128.0
 111.245.48.0
 109.21.244.0

และแปลงเป็นรูปแบบนี้โดยใช้ไพพ์อยู่ระหว่าง (สร้าง IP)

134.27.128.0 | 111.245.48.0 | 109.21.244.0 | 103.22.200.0/22

ฉันคิดว่ามันเป็นคำสั่งค้นหาและแทนที่sedแต่ฉันไม่สามารถใช้งานได้


3
คุณแค่ต้องการtrเพิ่มบรรทัดใหม่ลงใน|ท่อหรือไม่ ชอบ<ipfile tr \\n \| >outfileไหม
mikeserv

|จำเป็นต้องใช้พื้นที่โดยรอบหรือไม่?
cuonglm

2
@uselesslinuxman - ไม่ <คุณจะต้องเปลี่ยนเส้นทางการป้อนข้อมูล <mydoc tr \\n \| >mydoc2ดังนั้น แต่นั่นจะไม่ทำให้คุณมีช่องว่าง สำหรับผู้ที่อาจเป็นทางออกที่เร็วที่สุดคือpaste -d' | ' mydoc /dev/null /dev/null >mydoc2
mikeserv

1
@mikeserv: ฉันไม่คิดว่ามันจะทำงานได้ pasteเขียนบรรทัดที่เกี่ยวข้องจากแต่ละไฟล์ หากไม่มี-sคุณจะได้รับจำนวนบรรทัดกลับในไฟล์
cuonglm

2
@ val0x00ff: ฉันขอเชิญคุณให้อ่านunix.stackexchange.com/q/169716/38906
cuonglm

คำตอบ:


16

ใช้ sed บนพื้นฐานของการมีชื่อเสียง Sed หนึ่งสมุทรอธิบาย, Part 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 ขอโทษที่เป็นประเภท - แก้ไขขอบคุณ
steeldriver

มันใช้งานไม่ได้จริง ๆ โชคไม่ดี อย่างน้อยไม่ใช่สำหรับสตรีมแบบไม่ จำกัด เมื่อคุณทำสิ่งนี้คุณจะต้องกลืนอินพุททั้งหมดของคุณทีละบรรทัดและไม่สามารถเขียนแม้แต่ไบต์เดียวของมันเพื่อเอาท์พุทจนกว่าคุณจะได้ย่อยมันทั้งหมด - ทั้งหมดจะถูกแปลงเป็นบรรทัดเดียว มันเป็นเรื่องเทอะทะและมีแนวโน้มที่จะ segfault
mikeserv

IP หนึ่งล้านคือ <16 ล้านคุณต้องมีรายการใหญ่มากเพื่อ จำกัด ขอบเขตที่นี่ การใช้การค้นหาการตรวจจับ eof เป็นปัญหามากขึ้นเนื่องจากจะเป็นการรัน O (N ^ 2) กับขนาดไฟล์อินพุต sed 'H;1h;$!d;x;s/\n/ | /g'เป็นเส้นตรง
jthill

@jthill - POSIX รับประกันsedพื้นที่รูปแบบเพียง8K เท่านั้น มันน้อยกว่า 16M มาก
mikeserv

9

ฉันอยากรู้ว่าบางส่วนของเหล่านี้ (+ ทางเลือกบางอย่าง) ทำงานได้เร็วด้วยไฟล์ที่ค่อนข้างใหญ่ ( 163MiBหนึ่งไฟล์IPต่อบรรทัด, ~ 13 ล้านบรรทัด):

wc -l < iplist
13144256

ผลลัพธ์ (ด้วยsync; echo 3 > /proc/sys/vm/drop_cachesหลังจากแต่ละคำสั่งฉันทำการทดสอบซ้ำ - ตามลำดับย้อนหลัง - หลังจากสองสามชั่วโมง แต่ความแตกต่างนั้นเล็กน้อยมากโปรดทราบว่าฉันใช้อยู่gnu sed):

steeldriver :
ช้ามาก ยกเลิกหลังจากรอสองนาที ... ดังนั้นจึงไม่มีผลลัพธ์สำหรับสิ่งนี้

cuonglm :

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

jthill :

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

real    0m4.903s

Avinash Raj :

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:

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

real    0m4.543s

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

real    0m5.511s

Perl:

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

real    0m9.646s

xargs:

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

real    0m6.326s

การรวมกันของ head + paste + tr + cat:

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

real    0m0.991s

หากคุณมีGNU coreutilsและหากรายการ IP ของคุณไม่ใหญ่มาก (สมมติว่าสูงถึง 50,000 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

don - คุณสามารถเพิ่มคำแนะนำในคำถามโดย @ val0x00ff สำหรับwhile ... readลูปได้หรือไม่ ฉันอยากรู้ว่าสิ่งใดที่ 163k read()และการwrite()โทรแปลเป็นมาตรฐาน คำตอบที่ดีโดยวิธีการ
mikeserv

1
@ mikeserv - ไม่มีปัญหาฉันจะทำมัน (มันจะช้าจริงๆ )
don_crissti

นั่นเป็นลิงค์ที่ยอดเยี่ยมจริงๆ โดยเฉพาะอย่างยิ่งฉันชอบที่ผู้เขียนเสนอลิงค์ไปยังมาตรฐานที่คล้ายกัน 6 ปีที่นั่นเช่นกัน คุณสังเกตเห็นว่าsedดูเหมือนว่าจะมีสถานะที่ดีขึ้นในเวลานั้น(และอาจมีการเปลี่ยนแปลงเพียงเล็กน้อยของเครื่องยนต์ regexp)แต่grepดูเหมือนว่าจะลดลงอย่างมากในประสิทธิภาพการทำงานของมัน(โดยเฉพาะอย่างยิ่งสำหรับสายที่ยาวกว่า) ? ผมสงสัยว่าถ้าperlส่งไปยังเครื่องยนต์มีผลใด ๆ เกี่ยวกับผลลัพธ์เหล่านั้น ... นอกจากนี้ยังเป็นระเบียบที่dashไม่ได้เป็นสุดซึ้ง bashที่นี่มีแนวโน้มที่จะไกลช้า w / ทั่วไปIFS=เพิ่มด้านหน้า
mikeserv

อืม ... ลิงค์นั้นเป็นอีกหนึ่งตัวบ่งชี้ที่แข็งแกร่งที่ฉันจำเป็นต้องคาดเข็มขัดลงและเรียนรู้ C ดังนั้นในที่สุดฉันก็สามารถเริ่มใช้งาน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

@mikeserv: ยินดีต้อนรับ ดังที่ don_crissti แสดงไว้ในมาตรฐานของเขาการpasteแก้ปัญหาเป็นวิธีที่เร็วที่สุด
cuonglm

ผลลัพธ์ไม่ได้จบด้วยการขึ้นบรรทัดใหม่ คุณอาจจะต้องเปลี่ยนORS=""ภายในENDบล็อกORS="\n"เพื่อที่จะไม่
phk

4

ดังนั้นผมจึงมีสิ่งที่ผิดทั้ง - pasteและคำถามนี้ได้สอนให้ฉันมากเกี่ยวกับ ในฐานะที่เป็น cuonglm บันทึกอย่างถูกต้องเว้นแต่คุณจะpasteอยู่ในไฟล์ใน-serial คุณจะ\nจบลงด้วย w / ewline สุดท้ายจากรายการ infile ของคุณถูกผนวกเข้ากับเอาต์พุตตามที่เขียน ฉันเข้าใจผิดว่าเชื่อว่าpaste -sพฤติกรรมเป็นโหมดเริ่มต้น - และนี่เป็นความเข้าใจผิดที่เห็นได้ชัดว่าbusybox pasteมีความสุขที่ได้เสริมกำลัง คำสั่งต่อไปนี้ทำงานได้ตามโฆษณา w / busybox:

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

มันไม่ทำงานตามสเป็คแม้ว่า การใช้งานอย่างถูกต้องpasteจะยังคงต่อท้าย\newline สำหรับแต่ละลำดับเขียน ถึงกระนั้นก็ไม่ใช่เรื่องใหญ่เลย:

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

@don_crissti - dangit แท็บเล็ตโง่ ฉันเดาว่าสิ่งที่ชัดเจนที่ต้องทำคือสองน้ำพริก
mikeserv

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 ท่อต่อท้าย จะมีเพียง 2 ที่ส่วนท้ายหากอินพุตสิ้นสุดด้วยบรรทัดว่าง (สองบรรทัดใหม่)
JigglyNaga

3

ใช้ประโยชน์vim:

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

คำอธิบาย:

-n ปิดการใช้งานไฟล์ swap

-u NONE ถูกใช้เพื่อข้ามการเริ่มต้นทั้งหมด

-c {command} รันคำสั่งหลังจากอ่านไฟล์แล้ว

1,$-1s/\n/ | /gคือs/\n/ | /g(แทนที่ newline ด้วย space pipe space) สำหรับช่วง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

เพื่อความสมบูรณ์นี่คืออีกawkโซลูชันที่ใช้โซลูชั่นนี้ไม่ได้ใช้งานORSเลย:

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

สำหรับคำอธิบายดูโพสต์ของฉันที่/unix//a/338121/117599

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.