การประมวลผลข้อความ - เข้าร่วมทุก ๆ สองบรรทัดด้วยเครื่องหมายจุลภาค


35

ฉันมีมากกว่า 1,000 บรรทัดในไฟล์ ไฟล์เริ่มต้นดังต่อไปนี้ (เพิ่มหมายเลขบรรทัด):

Station Name
Station Code
A N DEV NAGAR
ACND
ABHAIPUR
AHA
ABOHAR
ABS
ABU ROAD
ABR

ฉันต้องแปลงไฟล์นี้เป็นไฟล์โดยคั่นรายการด้วยเครื่องหมายจุลภาคโดยเข้าร่วมทุกสองบรรทัด ข้อมูลสุดท้ายควรมีลักษณะดังนี้

Station Name,Station Code
A N DEV NAGAR,ACND
ABHAIPUR,AHA
ABOHAR,ABS
ABU ROAD,ABR
...

สิ่งที่ฉันพยายามคือ - พยายามเขียนเชลล์สคริปแล้วก็echoใช้คอมม่าคั่นกลาง แต่ผมคิดว่าง่ายมีประสิทธิภาพหนึ่งซับจะทำผลงานได้ที่นี่อาจจะอยู่ใน/sedawk

ความคิดใด ๆ


@ l0b0 คุณแก้ไขคำพูดของ OP ว่าหมายเลขบรรทัดนั้น "มีไว้เพื่ออธิบายเท่านั้น" ...
jasonwryan

@ jasonwryan ขออภัยฉันคิดว่ามีบรรทัดสำหรับคำอธิบาย การแยกวิเคราะห์ข้อผิดพลาดที่บรรทัด 0
l0b0

คำตอบ:


39

เพียงใช้cat(ถ้าคุณชอบแมว ;-)) และpaste:

cat file.in | paste -d, - - > file.out

คำอธิบาย: pasteอ่านจากไฟล์จำนวนหนึ่งและวางรวมบรรทัดที่เกี่ยวข้อง (บรรทัด 1 จากไฟล์แรกด้วยบรรทัด 1 จากไฟล์ที่สองเป็นต้น):

paste file1 file2 ...

แทนที่จะใช้ชื่อไฟล์เราสามารถใช้-(เส้นประ) pasteใช้บรรทัดแรกจาก file1 (ซึ่งเป็น stdin) จากนั้นมันต้องการอ่านบรรทัดแรกจาก file2 (ซึ่งก็คือ stdin) อย่างไรก็ตามเนื่องจากบรรทัดแรกของ stdin ถูกอ่านและประมวลผลแล้วสิ่งที่รออยู่ในอินพุตสตรีมคือบรรทัดที่สองของ stdin ซึ่งpasteมีความสุขติดอยู่กับบรรทัดแรก -dตัวเลือกชุดคั่นที่จะเป็นเครื่องหมายจุลภาคมากกว่าแท็บ

อีกทางเลือกหนึ่งทำ

cat file.in | sed "N;s/\n/,/" > file.out

ป.ล. ใช่หนึ่งสามารถลดความซับซ้อนด้านบนเพื่อ

< file.in sed "N;s/\n/,/" > file.out

หรือ

< file.in paste -d, - - > file.out

catซึ่งได้ประโยชน์จากการไม่ได้ใช้

อย่างไรก็ตามฉันไม่ได้ใช้สำนวนนี้อย่างตั้งใจเพื่อเหตุผลที่ชัดเจน - มันละเอียดมากและฉันชอบcat(CATS ARE NICE) ดังนั้นโปรดอย่าแก้ไข

อีกทางเลือกหนึ่งถ้าคุณต้องการวางเพื่อแมว (วางเป็นคำสั่งในการเรียงไฟล์ในแนวนอนในขณะที่แมวเรียงต่อกันในแนวตั้ง) คุณอาจใช้:

paste file.in | paste -d, - -

เพียงพูดถึงมันอีกครั้ง หมายเลขบรรทัดไม่ได้เป็นส่วนหนึ่งของไฟล์ :)
mtk

paste คำสั่งทำงานอย่างสมบูรณ์คุณสามารถโปรดให้คำอธิบายเล็ก ๆ น้อย ๆ เกี่ยวกับเรื่องนี้ ยัติภังค์
mtk

2
ยัติภังค์หมายถึง "อ่านจาก stdin" หากแหล่งอินพุตเดียวกันซ้ำแล้วซ้ำอีกให้วางรู้ที่จะอ่านหลาย ๆ ครั้งต่อแถวของเอาต์พุต
dubiousjim

@sch: เย็นแก้ไขผมจะได้สัมผัสมัน :-)
มกราคม

1
ด้วยความเคารพcatอาร์กิวเมนต์ของคุณ ใช้งานไม่sed "N;s/\n/,/" file.in > file.outได้?
แบร์นฮาร์ด

8

ในกรณีที่ผู้ที่เชื่อมโยงไปถึงที่นี่กำลังมองหาที่จะรวมทุกบรรทัดใน CSV หนึ่งซับลอง

cat file | tr '\n' ','


3
paste -sd ',\n' file.in > file.out

โปรดทราบว่าเนื่องจากเราเพียงแค่แทนที่อักขระหนึ่งตัวด้วยอักขระอื่น (ขึ้นบรรทัดใหม่ทุกบรรทัดด้วยเครื่องหมายจุลภาค) เราจึงสามารถทำงานกับไฟล์อินพุตได้:

paste -sd ',\n' file.in 1<> file.in

(แต่ระวังว่ามันอาจใช้ไม่ได้กับระบบที่ไม่ใช่ Unix ที่มี CRLF terminators (เช่นเดียวกับ Microsoft) ที่ POSIX จำลองบางตัวpasteอาจใช้งานในแบบที่ไม่ใช่ Unix)


อะไรที่1จะทำที่นี่ใน1<>? นั่นคือการพิมพ์ผิด?
αғsнιη

@ αғsнιηดูนี่
iruvar

@iruvar ขอบคุณ
αғsнιη

2

นี่คือหนึ่งซับ (แม้ว่าอาจจะหลายล้านคำสั่งรันเอ้อ) โดยใช้ Bash บริสุทธิ์:

(IFS=; while read -r name; do read -r code; printf '%s\n" "$name,$code"; done < file.in) > file.out

ผมใช้ subshell (วงเล็บ) IFSเพื่อที่ฉันจะไม่ได้มีการจัดเก็บและเรียกคืน สิ่งใดที่ควรทำเพื่อไม่ให้สับสนกับสภาพแวดล้อมของผู้ใช้ในกรณีที่แหล่งที่มา ทางเลือกที่จะผ่านที่ไอเอฟเอใหม่เท่านั้นที่จะreadเป็นใน,IFS= read -r nameIFS= read -r code

ข้อเท็จจริงที่ว่าคำสั่งทั้งหมดในลูปถูกสร้างขึ้นในเชลล์ทำให้ประสิทธิภาพการทำงานยอมรับได้และเร็วกว่าโซลูชันอื่นสำหรับไฟล์ขนาดเล็ก แต่หลายคนอาจคิดว่าเป็นการฝึกฝนที่ไม่ดีและควรระมัดระวังเมื่อพูดถึงเรื่องอื่น


โดยทั่วไปแล้วสำหรับการใช้ subshells เพื่อ จำกัด การเปลี่ยนแปลงสภาพแวดล้อม แต่ในกรณีนี้มันไม่จำเป็น: คุณสามารถทำแทนwhile IFS='\n' read -r name; do IFS='\n' read -r code ... done < file.inซึ่งเป็นสำนวนที่ฉันมักจะเห็นในเชลล์สคริปต์ การ-rตั้งค่าสถานะเพื่อreadหมายถึง "ตีความอักขระ '\' ตามด้วยอักขระ 'n' ในสตรีม stdin เป็นอักขระสองตัวแทนที่จะเป็นบรรทัดใหม่" เนื้อหามันอาจจะเป็นความงามมากขึ้นเพื่อสร้าง subshell IFS='\n'ที่คุณทำมากกว่าที่จะทำซ้ำ
dubiousjim

@dubiousjim: การ-rปรับปรุงการแก้ปัญหาทางเทคนิค ที่ดี! ฉันไม่ใช่แฟนของความคิดที่จะผ่านการเปลี่ยนแปลงIFSสองครั้ง ถ้าฉันเคยอ่านหนึ่งครั้งดีมาก แต่ไม่ใช่สองครั้ง แน่นอนว่าเป็นเรื่องของความคิดเห็น การใช้ subshell นั้นมากกว่าความรู้ทั่วไปของ Bash ที่ฉันพูดดังนั้นผู้คนจำนวนมากจะมีปัญหาในการทำความเข้าใจวัตถุประสงค์ของมัน นั่นเป็นสิ่งที่ไม่ดี
ลบ

2

สำหรับชุดของคำตอบที่สมบูรณ์awkอาจเป็นวิธีแก้ปัญหา:

awk 'NR%2==1 {printf $0","} NR%2==0 { print $0}' *file*

@downvoter: มีอะไรผิดปกติกับคำตอบของฉันที่สมควรได้รับ downvote? จะปรับปรุงได้อย่างไร?
แบร์นฮาร์ด

อาจเป็นเพราะคนขี้เกียจprintf? จะล้มเหลวในกรณีที่ไม่ค่อยเกิดขึ้นเมื่อชื่อสถานีมีตัวระบุรูปแบบ (ดูตัวอย่างจากpastebin.com/wgxFttrJ ) แต่นี่เป็นเพียงการคาดเดา downvote ไม่ได้มาจากฉัน
จัดการ

1

เกาลัดแก่แก่ของawkสำนวน

awk '{ORS=NR%2?",":"\n";print}' file
Station Name,Station Code
A N DEV NAGAR,ACND
ABHAIPUR,AHA
ABOHAR,ABS
ABU ROAD,ABR

awk '{ORS=NR%2?",":"\n"};1'สั้นและสำนวนมากขึ้น
cuonglm

@cuonglm ฉันสงสัยมัน ในกรณีนี้มันยังคงเป็นหนึ่งซับแม้จะมีprintและเจตนาที่ชัดเจน 1เห็นได้ชัดว่าawkprint
มือเก่า

นี่เป็นทางออกแรก ๆ ที่ฉันพบว่าสามารถกำหนดค่าได้ง่ายกว่า 2 บรรทัด ฉันต่อสู้ด้วยsedก่อนที่จะค้นหา แต่awkทำให้การรวมทุก ๆ 4 บรรทัดง่ายขึ้น ช่วยชีวิตฉันไปเที่ยว$EDITOR!
opello


0

ตัวอย่างเช่น:

seq 0 70 | xargs -L 2 | sed 's/ /,/g'

เอาต์พุต: (หมายเหตุ: xargs -L number_of_columnsทำงานได้ดีกับคอลัมน์จำนวนมากที่สุดไม่ใช่แค่ทุกสองบรรทัด)

0,1
2,3
4,5
6,7
8,9
10,11
12,13
14,15
16,17
18,19
20,21
22,23
24,25
26,27
28,29
30,31
32,33
34,35
36,37
38,39
40,41
42,43
44,45
46,47
48,49
50,51
52,53
54,55
56,57
58,59
60,61
62,63
64,65
66,67
68,69
70

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