วิธีการรวมทุกสองบรรทัดเป็นหนึ่งเดียวจากบรรทัดคำสั่ง?


151

ฉันมีไฟล์ข้อความที่มีรูปแบบดังต่อไปนี้ บรรทัดแรกคือ "KEY" และบรรทัดที่สองคือ "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ช้ากว่าช้ากว่าทุบตี !)
F. Hauri

คำตอบ:


182

awk:

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

หมายเหตุมีบรรทัดว่างที่ท้ายเอาต์พุต

sed:

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

ไม่ทำงานกับเอาต์พุตสี ฉันลองทุกอย่างในคำถาม & คำตอบนี้และไม่มีอะไรทำงานเมื่อผลลัพธ์เป็นสี ansi ทดสอบบน Ubuntu 13.04
Leo Gallucci

1
@elgalu: เนื่องจากสี ANSI เป็นเพียงชุดของการรวมตัวอักขระยกเว้น ทำ hexedit บนเอาต์พุตเพื่อดูว่าคุณมีอะไร
not2qubit

7
วิธีการแก้ปัญหา awk นี้สามารถทำลายถ้าprintfสตริงการขยายตัวชอบที่พบภายใน%s $0ความล้มเหลวนั้นสามารถหลีกเลี่ยงได้ดังนี้:'NR%2{printf "%s ",$0;next;}1'
ghoti

9
เพราะมันยากที่ google จริง ๆ การจัดฟัน1หลังปิดหมายความว่าอะไร
erikbwork

5
@ erikb85 คุณไปที่stackoverflow.com/questions/24643240/…
Viraj

243

paste ดีสำหรับงานนี้:

paste -d " "  - - < filename

10
ฉันคิดว่านี่เป็นทางออกที่ดีที่สุดที่นำเสนอแม้จะใช้ทั้งแบบไร้หรือไร้สาระ ในการป้อนข้อมูลที่มีจำนวนบรรทัดคี่โซลูชั่น awk ของเคนท์จะขึ้นบรรทัดใหม่ขั้นสุดท้ายโซลูชัน sed ของเขาจะข้ามบรรทัดสุดท้ายใน entirty และโซลูชันของฉันจะทำซ้ำบรรทัดสุดท้าย pasteในทางกลับกันทำงานได้อย่างสมบูรณ์แบบ +1
ghoti

8
ฉันมักจะใช้แต่มักจะลืมเกี่ยวกับcut pasteมันสั่นสะเทือนสำหรับปัญหานี้ ฉันต้องการที่จะรวมทุกบรรทัดจาก stdin paste -sd ' ' -และทำมันได้อย่างง่ายดายด้วย
Clint Pachl

4
เรียบง่ายและสวยงาม!
krlmlr

8
so -mean stdin, ดังนั้นpaste - -mean read จาก stdin, จากนั้นอ่านจาก stdin, คุณสามารถสแต็คพวกมันได้มากเท่าที่คุณต้องการ
ThorSummoner

1
ใช่ @ThorSummoner ... ฉันต้องวางทุกสามบรรทัดในบรรทัดเดียวและวาง - - - และทำงานได้อย่างสมบูรณ์
Daniel Goldfarb

35

ทางเลือกที่จะ sed, awk, grep:

xargs -n2 -d'\n'

สิ่งนี้จะดีที่สุดเมื่อคุณต้องการเข้าร่วม N บรรทัดและคุณต้องการเพียงช่องว่างที่มีตัวคั่น

คำตอบเดิมของฉันคือการxargs -n2แยกคำมากกว่าบรรทัด -dสามารถใช้เพื่อแยกอินพุตด้วยอักขระตัวเดียว


4
นี่เป็นวิธีการที่ดี แต่ใช้กับคำพูดไม่ใช่บรรทัด เพื่อให้มันทำงานบนบรรทัดสามารถเพิ่มได้-d '\n'
Don Hatch

2
ว้าวฉันเป็นxargsผู้ใช้ทั่วไปแต่ไม่รู้สิ่งนี้ สุดยอดเคล็ดลับ
Sridhar Sarnobat

1
ฉันรักสิ่งนี้. สะอาดมาก
Alexander Guo

28

มีวิธีฆ่าสุนัขมากกว่าแขวน [1]

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

ใส่ตัวคั่นอะไรก็ได้ที่คุณชอบในเครื่องหมายคำพูด


อ้างอิง:

  1. เดิมที "มีวิธีการมากมายในการสกินแมว" ให้ย้อนกลับไปสู่การแสดงออกที่เก่ากว่าและมีต้นกำเนิดที่อาจไม่มีอะไรเกี่ยวข้องกับสัตว์เลี้ยง

ฉันรักทางออกนี้
luis.espinal

5
ในฐานะเจ้าของแมวฉันไม่ชอบอารมณ์ขันแบบนี้
witkacy26

4
@ witkacy26 นิพจน์ที่ปรับเปลี่ยนตามความกังวลของคุณ
ghoti

ฉันชอบวิธีแก้ปัญหาที่
แย่

@Rubendob - awk $0อ่านแต่ละบรรทัดของการป้อนข้อมูลและสถานที่ในตัวแปร getlineคำสั่งยังคว้า "ต่อไป" $0สายของการป้อนข้อมูลและสถานที่ใน ดังนั้นคำสั่งแรกคว้าบรรทัดแรกและ concatenates คำสั่งพิมพ์สิ่งที่ถูกบันทึกไว้ในตัวแปรที่มีสตริงที่มีเครื่องหมายจุลภาคพร้อมกับสายที่จะถูกดึงข้อมูลโดยใช้key getlineชัดเจน? :)
ghoti


11

แม้ว่ามันจะดูเหมือนว่าการแก้ปัญหาก่อนหน้านี้จะทำงานได้หากมีความผิดปกติเกิดขึ้นเพียงครั้งเดียวในเอกสารเอาต์พุตจะไปเป็นชิ้น ๆ ด้านล่างนี้ค่อนข้างปลอดภัยกว่า

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

3
ทำไมจึงปลอดภัยกว่า อะไร/KEY/ทำอย่างไร สิ่งที่จะpทำในตอนท้าย?
Stewart

ค้นหาบรรทัดที่มี/KEY/ พิมพ์ผลออกมา มันปลอดภัยกว่าเพราะจะใช้การดำเนินการกับบรรทัดที่มีเครื่องหมายอยู่ในนั้นเท่านั้น KEYpKEY
minghua

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

ตามที่ระบุโดยEd Mortonในความคิดเห็นจะดีกว่าการเพิ่มเครื่องหมายวงเล็บเพื่อความปลอดภัยและ parens สำหรับพกพา

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

ORSย่อมาจาก Output Record Separator สิ่งที่เราทำที่นี่คือการทดสอบเงื่อนไขโดยใช้NRที่เก็บหมายเลขบรรทัด หาก modulo ของNRเป็นค่าจริง (> 0) จากนั้นเราตั้งค่าตัวคั่นฟิลด์ผลลัพธ์เป็นค่าของFS(ตัวคั่นฟิลด์) ซึ่งโดยค่าเริ่มต้นคือช่องว่างมิฉะนั้นเราจะกำหนดค่าของRS(ตัวคั่นบันทึก) ซึ่งเป็นบรรทัดใหม่

หากคุณต้องการเพิ่ม ,เป็นตัวคั่นให้ใช้รายการต่อไปนี้:

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

1
วิธีการที่ถูกต้องแน่นอนดังนั้น +1 แต่ฉันสงสัยว่าเงื่อนไขคืออะไรที่ถูกประเมินเพื่อเรียกการกระทำเริ่มต้นของการพิมพ์บันทึก มันเป็นงานที่ประสบความสำเร็จหรือไม่ มันเป็นเรื่องง่ายORSและเป็นเรื่องที่ถือว่าเป็นtrueเพราะ ORS ได้รับค่าที่ไม่เป็นศูนย์หรือสตริงว่างและ awks คาดเดาอย่างถูกต้องว่ามันควรจะต่อยแทนการเปรียบเทียบตัวเลข? มันเป็นอย่างอื่น? awk '{ORS=(NR%2?FS:RS)}1' fileฉันจริงๆไม่แน่ใจและดังนั้นฉันได้เขียนเป็น ฉันวงเล็บนิพจน์ประกอบไปด้วยเพื่อให้แน่ใจว่าพกพาได้ดี
เอ็ดมอร์ตัน

1
@EdMorton ใช่ฉันเพิ่งเห็น upvotes สองสามคำตอบนี้กำลังจะปรับปรุงเพื่อรวมวงเล็บปีกกาเพื่อความปลอดภัย จะเพิ่ม parens เช่นกัน
jaypal singh

7

"ex" เป็นเครื่องมือแก้ไขบรรทัดสคริปต์ที่อยู่ในตระกูลเดียวกันกับ sed, awk, grep และอื่น ๆ ฉันคิดว่าอาจเป็นสิ่งที่คุณกำลังมองหา vi / ผู้สืบทอดที่ทันสมัยหลายคนยังมีโหมด vi

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

สิ่งนี้บอกว่าสำหรับแต่ละบรรทัดหากตรงกับ "KEY" ให้ดำเนินการj oin ของบรรทัดต่อไปนี้ หลังจากคำสั่งนั้นเสร็จสมบูรณ์ (เทียบกับทุกบรรทัด) ให้ออกw rite และq uit


4

หาก Perl เป็นตัวเลือกคุณสามารถลอง:

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

ที่ไม่-0Perl บอกการตั้งคั่นบันทึก ( $/)โมฆะเพื่อให้เราสามารถขยายสายหลายในรูปแบบการจับคู่ของเรา manpages เป็นบิตเทคนิคเกินไปสำหรับผมที่จะคิดออกว่ามันหมายความว่าในทางปฏิบัติ..
Sridhar Sarnobat

4

คุณสามารถใช้ awk เช่นนี้เพื่อรวมสายคู่ที่ 2:

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

4

โซลูชันอื่นที่ใช้ vim (สำหรับการอ้างอิงเท่านั้น)

โซลูชันที่ 1 :

เปิดไฟล์เป็นกลุ่มvim filenameจากนั้นเรียกใช้คำสั่ง:% normal Jj

คำสั่งนี้เข้าใจง่ายมาก:

  • %: สำหรับทุกบรรทัด
  • ปกติ: รันคำสั่งปกติ
  • Jj: รันคำสั่ง Join จากนั้นข้ามไปยังบรรทัดด้านล่าง

หลังจากนั้นให้บันทึกไฟล์และออกด้วย :wq

โซลูชันที่ 2 :

รันคำสั่งในเปลือกแล้วบันทึกไฟล์และออกด้วยvim -c ":% normal Jj" filename:wq


นอกจากนี้ยังnorm!มีประสิทธิภาพมากขึ้นว่าnormalในกรณีที่Jแมปใหม่ +1 สำหรับกลุ่มโซลูชั่น
qeatzy

@qeatzy ขอบคุณสำหรับการสอนฉันว่า ดีใจมากที่รู้ว่ามัน ^ _ ^
Jensen

3

คุณยังสามารถใช้คำสั่ง vi ต่อไปนี้:

:%g/.*/j

หรือแม้กระทั่ง:%g//jเนื่องจากสิ่งที่คุณต้องการคือการจับคู่สำหรับการเข้าร่วมที่จะดำเนินการและสตริง null ยังคงเป็น regex ที่ถูกต้อง
ghoti

1
@ghoti ในกลุ่มเมื่อใช้แค่//รูปแบบการค้นหาก่อนหน้านี้จะถูกนำมาใช้แทน หากไม่มีรูปแบบก่อนหน้าเป็นกลุ่มเพียงรายงานข้อผิดพลาดและไม่ทำอะไรเลย โซลูชันของ Jdamian ใช้ได้ตลอดเวลา
Tzunghsing David Wong

1
@TzunghsingDavidWong - นั่นเป็นตัวชี้ที่ดีสำหรับผู้ใช้ที่เป็นกลุ่ม มีประโยชน์สำหรับฉันทั้งคำถามและคำตอบที่กล่าวถึงเป็นกลุ่ม
ghoti

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

สิ่งนี้ไม่สามารถทำได้คือใช้ตัวคั่นที่ประกอบด้วยอักขระหลายตัว

โบนัสหากpasteเป็นไปตาม POSIX สิ่งนี้จะไม่แก้ไขบรรทัดใหม่ของบรรทัดสุดท้ายในไฟล์ดังนั้นสำหรับไฟล์อินพุตที่มีจำนวนบรรทัดเช่น

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. ลบบรรทัดคู่และเขียนในไฟล์ temp 1
  2. ลบบรรทัดคี่และเขียนในไฟล์ชั่วคราวบางไฟล์ 2
  3. รวมสองไฟล์เข้าด้วยกันโดยใช้คำสั่ง paste กับ -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

-0gobbles ทั้งไฟล์แทนที่จะอ่านมันทีละบรรทัด
pEล้อมรอบโค้ดด้วยลูปและพิมพ์เอาต์พุตดูรายละเอียดในhttp://perldoc.perl.org/perlrun.html ;
^KEYจับคู่ "KEY" ในตอนต้นของบรรทัดตามด้วยการจับคู่แบบไม่โลภของอะไร ( .*?) ก่อนลำดับของ

  1. ช่องว่างหนึ่งช่องขึ้นไป \s+ชนิดรวมถึงตัวแบ่งบรรทัด
  2. หนึ่งหลักหรือมากกว่า(\d+)ที่เราจับภาพและภายหลังใส่ใหม่เป็น$1;

$ตามด้วยจุดสิ้นสุดของบรรทัด

\Kสิ่งอำนวยความสะดวกไม่รวมทุกอย่างบนด้านซ้ายมือจากทดแทนเพื่อ{ $1}แทนที่เพียง 1-2 ลำดับดูhttp://perldoc.perl.org/perlre.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";

คำตอบนี้ไม่ได้เพิ่มสิ่งใด ๆ ที่ไม่ได้ให้ไว้ในคำตอบของ Hai Vuที่โพสต์เมื่อ 4 ปีก่อนหน้าคุณ
fedorqui 'ดังนั้นหยุดการทำร้าย'

ฉันเห็นด้วยบางส่วนฉันพยายามที่จะเพิ่มคำอธิบายและทั่วไปมากขึ้นมันจะไม่แก้ไขไฟล์เก่าเช่นกัน ขอบคุณสำหรับคำแนะนำของคุณ
Suman

-2

คุณสามารถใช้xargsสิ่งนี้:

xargs -a file

% cat> file abc% xargs -a file abc เหมาะกับฉัน
RSG

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