เชื่อมหลายไฟล์เข้าด้วยกันด้วยส่วนหัวเดียวกัน


26

ฉันมีหลายไฟล์ที่มีส่วนหัวเดียวกันและเวกเตอร์ต่างกันด้านล่าง ฉันต้องต่อกันทั้งหมด แต่ฉันต้องการเฉพาะส่วนหัวของไฟล์แรกที่จะต่อกันและฉันไม่ต้องการให้ส่วนหัวอื่นถูกต่อกันเนื่องจากมันเหมือนกันทั้งหมด

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

<header>INFO=<ID=DP,Number=1,Type=Integer>
<header>INFO=<ID=DP4,Number=4,Type=Integer>
A
B 
C

file2.txt

<header>INFO=<ID=DP,Number=1,Type=Integer>
<header>INFO=<ID=DP4,Number=4,Type=Integer>
D
E 
F

ฉันต้องการผลลัพธ์ที่จะเป็น

<header>INFO=<ID=DP,Number=1,Type=Integer>
<header>INFO=<ID=DP4,Number=4,Type=Integer>
A
B
C
D
E 
F

ฉันสามารถเขียนสคริปต์ใน R แต่ฉันต้องการมันในเปลือก?

คำตอบ:


17

หากคุณรู้วิธีที่จะทำใน R โดยวิธีทั้งหมดทำในอาร์ด้วยเครื่องมือยูนิกซ์แบบคลาสสิกนี่เป็นการกระทำที่เป็นธรรมชาติที่สุดใน awk

awk '
    FNR==1 && NR!=1 { while (/^<header>/) getline; }
    1 {print}
' file*.txt >all.txt

บรรทัดแรกของสคริปต์ awk ตรงกับบรรทัดแรกของไฟล์ ( FNR==1) ยกเว้นว่าเป็นบรรทัดแรกของไฟล์ทั้งหมด ( NR==1) เมื่อเงื่อนไขเหล่านี้จะพบการแสดงออกwhile (/^<header>/) getline;จะถูกดำเนินการซึ่งสาเหตุ awk เพื่อให้อ่านบรรทัดอื่น (ข้ามหนึ่งในปัจจุบัน) ตราบใดที่หนึ่งในปัจจุบันตรงกับ ^<header>regexp บรรทัดที่สองของสคริปต์ awk พิมพ์ทุกอย่างยกเว้นบรรทัดที่ข้ามไปก่อนหน้านี้


ขอบคุณ Gilles ไฟล์ของฉันแต่ละไฟล์มีหน่วยเป็น GB R จะไม่มีประสิทธิภาพทำเช่นนี้ นั่นเป็นเหตุผลที่ฉันถาม
Jana

@Jana มีบรรทัดที่ดูเหมือนส่วนหัว แต่ไม่อยู่ที่ด้านบนของไฟล์หรือไม่ ถ้าไม่วิธีที่เร็วที่สุดคือการใช้grep(เช่นในคำตอบของสปุตนิก )
Gilles 'หยุดความชั่วร้าย'

ไม่มีบรรทัดส่วนหัวคล้ายกับไฟล์ทั้งหมดและอยู่ที่ด้านบนของแต่ละไฟล์ ใช่ grep เร็วขึ้น ขอบคุณทั้งคู่
Jana

1
@Jana อย่างไรก็ตามถ้าไฟล์ทั้งหมดของคุณมีจำนวนบรรทัดส่วนหัวเท่ากันนี่เป็นอีกวิธีหนึ่ง (ซึ่งฉันคาดว่าจะเร็วกว่า): head -n 10 file1.txt >output.txt && tail -q -n +11 file*.txt >>output.txt(ถ้าคุณมี 10 บรรทัดส่วนหัว) นอกจากนี้ถ้าไฟล์ของคุณมีตัวเลขในชื่อของพวกเขาระวังว่าfile9.txtจะถูกจัดเรียงระหว่างและfile89.txt file90.txtหากไฟล์ของคุณได้หมายเลขชอบfile001.txt, ... files009.txt, files010.txt... แล้วfiles*.txtจะแสดงรายการไว้ในลำดับที่ถูกต้อง
Gilles 'SO- หยุดความชั่วร้าย'

ทางออกที่ดีกว่า (จากstackoverflow.com/a/16890695/310441 ) ที่ไม่จำเป็นต้องใช้การจับคู่ regex: awk 'FNR==1 && NR!=1{next;}{print}' *.csv
Owen

42

โซลูชันอื่นซึ่งคล้ายกับ " cat+grep" จากด้านบนการใช้tailและhead:

  1. เขียนส่วนหัวของไฟล์แรกลงในเอาต์พุต:

    head -2 file1.txt > all.txt

    - head -2รับ 2 บรรทัดแรกของไฟล์

  2. เพิ่มเนื้อหาของไฟล์ทั้งหมด:

    tail -n +3 -q file*.txt >> all.txt

    - -n +3ทำให้tailเส้นพิมพ์จาก 3 ท้ายที่สุด -qจะบอกว่ามันไม่ได้ที่จะพิมพ์ส่วนหัวที่มีชื่อไฟล์ (อ่านman) เพื่อเพิ่มไฟล์ไม่เขียนทับมันเป็น>>>

และแน่ใจว่าคุณสามารถใส่ทั้งสองคำสั่งในหนึ่งบรรทัด:

head -2 file1.txt > all.txt; tail -n +3 -q file*.txt >> all.txt

หรือแทนที่จะ;ใส่&&ระหว่างพวกเขาเพื่อตรวจสอบความสำเร็จ


3
ฉันขอแนะนำให้เพิ่มเติมง่ายๆเพียงไปที่: (head -2 file1.txt ; tail -n +3 -q file*.txt ) > all.txtหรือ(head -2 file1.txt && tail -n +3 -q file*.txt ) > all.txt
HongboZhu

4

ลองทำสิ่งนี้:

$ cat file1.txt; grep -v "^<header" file2.txt
<header>INFO=<ID=DP,Number=1,Type=Integer>
<header>INFO=<ID=DP4,Number=4,Type=Integer>
A
B 
C
D
E 
F

บันทึก

  • -vธงหมายถึงการกลับการแข่งขันของ
  • ^ในREGEXหมายถึงจุดเริ่มต้นของสตริง
  • หากคุณมีไฟล์จำนวนมากคุณสามารถทำได้

:

array=( files*.txt )
{ cat ${array[@]:0:1}; grep -v "^<header" ${array[@]:1}; } > new_file.txt

มันเป็นเทคนิคการแบ่งแถวของ


ขอบคุณ sputnick แต่ฉันมีไฟล์ 30 ไฟล์ (file1.txt, file2.txt, file3.txt ..filen.txt) ที่จะต่อกัน ฉันควรพิมพ์ทุกชื่อไฟล์หรือมีวิธีอื่นที่จะทำหรือไม่?
Jana

ดูโพสต์ที่แก้ไขของฉันด้วยเทคนิคการแบ่ง
Gilles Quenot

สิ่งนี้จะลบ<header>บรรทัดที่ใดก็ได้ในไฟล์ไม่ใช่เฉพาะที่จุดเริ่มต้น นี่อาจไม่ใช่ปัญหาที่นี่ขึ้นอยู่กับข้อมูล
Gilles 'หยุดความชั่วร้าย'

1
เรียบง่าย:grep '^<header>' file1.txt >output.txt && grep -v '^<header>' file*.txt >>output.txt
Gilles 'SO- หยุดความชั่วร้าย'

@Gilles: ฉันสังเกตเห็นคำตอบของคุณหลังจากเวลานาน แต่มันมีประโยชน์มาก
Jana

1

tailคำสั่ง (บน GNU อย่างน้อย) มีตัวเลือกที่จะข้ามจำนวนที่กำหนดของเส้นเริ่มต้น หากต้องการพิมพ์จากบรรทัดที่สองเป็นต้นไปเช่นข้ามส่วนหัวหนึ่งบรรทัดให้ทำ:tail -n+2 myfile

ดังนั้นเพื่อให้ส่วนหัวสองบรรทัดของไฟล์แรก แต่ไม่ใช่ส่วนที่สองใน Bash:

cat file1.txt <(tail -n+3 file2.txt) > combined.txt

หรือสำหรับไฟล์จำนวนมาก:

head -n1 file1.txt > combined.txt
for fname in *.txt
do
    tail -n+3 $fname >> combined.txt
done

หากทราบว่ามีสตริงที่แน่นอนในบรรทัดส่วนหัวทั้งหมด แต่ไม่เคยอยู่ในไฟล์อินพุตที่เหลือgrep -vจะเป็นวิธีที่ง่ายกว่าดังที่ sputnik แสดง


1

สั้นลง (ไม่จำเป็นต้องเร็วกว่า) ด้วยsed:

sed -e '3,${/^<header>/d' -e '}' file*.txt > all.txt

การดำเนินการนี้จะลบทุกบรรทัดที่ขึ้นต้นด้วยการ<header>...เริ่มจากบรรทัดที่ 3 ดังนั้นส่วนหัวแรกจะถูกเก็บไว้และส่วนหัวอื่นจะถูกลบออก หากมีจำนวนบรรทัดที่แตกต่างกันในส่วนหัวให้ปรับคำสั่งตามนั้น (เช่นสำหรับการใช้ส่วนหัว 6 บรรทัด7แทน3)
หากไม่ทราบจำนวนบรรทัดในส่วนหัวคุณสามารถลองดังนี้

sed '1{
: again
n
/^<header>/b again
}
/^<header>/d
' file*.txt > all.txt

0

array = (* .txt); head -1 $ {array [0]}> all.txt; tail -n +2 -q $ {array [@]: 0} >> all.txt

สมมติว่าคุณกำลังใช้โฟลเดอร์ที่มีไฟล์. txt ที่มีส่วนหัวเดียวกับที่ต้องรวม / ตัดแบ่งรหัสนี้จะรวมไฟล์ txt ทั้งหมดลงในall.txtด้วยส่วนหัวเดียว บรรทัดแรก (บรรทัดคั่นด้วยเซมิโคลอน) รวบรวมไฟล์ข้อความทั้งหมดเพื่อต่อกันบรรทัดที่สองส่งเอาต์พุตส่วนหัวจากไฟล์ txt แรกไปยังall.txtและบรรทัดสุดท้ายเชื่อมต่อไฟล์ข้อความทั้งหมดที่รวบรวมโดยไม่มีส่วนหัว (โดยเริ่มต้น เรียงต่อกันจากแถว 2 เป็นต้นไป) และผนวกมันall.txt


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