Ghép nhiều tệp với cùng một tiêu đề


26

Tôi có nhiều tệp có cùng tiêu đề và các vectơ khác nhau bên dưới. Tôi cần nối tất cả chúng nhưng tôi chỉ muốn tiêu đề của tệp đầu tiên được nối và tôi không muốn các tiêu đề khác được nối với nhau vì chúng đều giống nhau.

ví dụ: 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

Tôi cần đầu ra

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

Tôi có thể viết một tập lệnh bằng R nhưng tôi cần nó trong shell?

Câu trả lời:


17

Nếu bạn biết cách thực hiện trong R, thì bằng mọi cách hãy thực hiện trong R. Với các công cụ unix cổ điển, điều này được thực hiện một cách tự nhiên nhất trong awk.

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

Dòng đầu tiên của tập lệnh awk khớp với dòng đầu tiên của tệp ( FNR==1) trừ khi đó cũng là dòng đầu tiên trên tất cả các tệp ( NR==1). Khi các điều kiện này được đáp ứng, biểu thức while (/^<header>/) getline;được thực thi, điều này khiến awk tiếp tục đọc một dòng khác (bỏ qua dòng hiện tại) miễn là dòng hiện tại khớp với biểu thức chính quy ^<header>. Dòng thứ hai của tập lệnh awk in mọi thứ trừ các dòng đã bị bỏ qua trước đó.


Cảm ơn Gilles. Mỗi tệp của tôi được tính bằng GB. R sẽ không hiệu quả làm điều này. Đó là lý do tại sao tôi hỏi.
Jana

@Jana Có những dòng trông giống như tiêu đề nhưng không ở đầu tập tin? Nếu không, cách nhanh nhất là sử dụng grep(như trong câu trả lời của sputnik ).
Gilles 'SO- ngừng trở nên xấu xa'

Không có dòng tiêu đề nào giống với tất cả các tệp và chúng chỉ ở đầu mỗi tệp. Vâng grep đã nhanh hơn. Cảm ơn cả hai bạn
Jana

1
@Jana Nhân tiện, nếu tất cả các tệp của bạn có cùng số dòng tiêu đề, thì đây là một cách khác (mà tôi dự đoán sẽ nhanh hơn nữa): head -n 10 file1.txt >output.txt && tail -q -n +11 file*.txt >>output.txt(nếu bạn có 10 dòng tiêu đề). Ngoài ra, nếu các tệp của bạn có số trong tên của chúng, hãy cẩn thận file9.txtđược sắp xếp giữa file89.txtfile90.txt. Nếu tập tin của bạn đã số thích file001.txt, ..., files009.txt, files010.txt, ..., sau đó files*.txtsẽ liệt kê chúng theo thứ tự đúng.
Gilles 'SO- ngừng trở nên xấu xa'

Một giải pháp tốt hơn (từ stackoverflow.com/a/16890695/310441 ) không yêu cầu kết hợp regex: awk 'FNR==1 && NR!=1{next;}{print}' *.csv
Owen

42

Một giải pháp khác, tương tự như " cat+grep" ở trên, sử dụng tailhead:

  1. Viết tiêu đề của tệp đầu tiên vào đầu ra:

    head -2 file1.txt > all.txt

    - head -2được 2 dòng đầu tiên của tập tin.

  2. Thêm nội dung của tất cả các tệp:

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

    - -n +3tạo tailcác dòng in từ thứ 3 đến cuối, -qbảo nó không in tiêu đề bằng tên tệp (đã đọc man), >>thêm vào tệp, không ghi đè lên dưới dạng >.

Và chắc chắn rằng bạn có thể đặt cả hai lệnh trong một dòng:

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

hoặc thay vì ;đặt &&giữa chúng để kiểm tra thành công.


3
Tôi đề nghị đơn giản hơn nữa là: (head -2 file1.txt ; tail -n +3 -q file*.txt ) > all.txthoặc(head -2 file1.txt && tail -n +3 -q file*.txt ) > all.txt
HongboZhu

4

Hãy thử làm điều này:

$ 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

CHÚ THÍCH

  • các -vphương tiện cờ để đảo ngược trận đấu của
  • ^trong REGEX , có nghĩa là bắt đầu chuỗi
  • nếu bạn có một loạt các tập tin, bạn có thể làm

:

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

Đó là một kỹ thuật cắt mảng .


Cảm ơn sputnick, nhưng tôi có ~ 30 tệp (file1.txt, file2.txt, file3.txt..filen.txt) để được nối. Tôi nên gõ mỗi tên tệp hoặc có cách nào khác để làm điều đó không?
Jana

Xem bài đăng đã chỉnh sửa của tôi với kỹ thuật cắt lát
Gilles Quenot

Điều này loại bỏ <header>các dòng bất cứ nơi nào trong các tập tin, không chỉ ở đầu. Điều này có thể không phải là một vấn đề ở đây, tùy thuộc vào dữ liệu.
Gilles 'SO- ngừng trở nên xấu xa'

1
Đơn giản hơn:grep '^<header>' file1.txt >output.txt && grep -v '^<header>' file*.txt >>output.txt
Gilles 'SO- ngừng trở nên xấu xa'

@Gilles: Tôi nhận thấy câu trả lời của bạn sau một thời gian dài nhưng nó rất hữu ích
Jana

1

Các taillệnh (trên GNU, ít nhất) có một tùy chọn để bỏ qua một số lượng nhất định của dòng ban đầu. Để in từ dòng thứ hai trở đi, tức là bỏ qua tiêu đề một dòng, hãy:tail -n+2 myfile

Vì vậy, để giữ tiêu đề hai dòng của tệp đầu tiên nhưng không phải là tiêu đề thứ hai, trong Bash:

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

Hoặc, đối với nhiều tệp:

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

Nếu một chuỗi nhất định được biết là có mặt trong tất cả các dòng tiêu đề nhưng không bao giờ trong phần còn lại của các tệp đầu vào, thì đó grep -vlà một cách tiếp cận đơn giản hơn, như sputnik đã chỉ ra.


1

Ngắn hơn (không nhất thiết phải nhanh hơn) với sed:

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

Điều này sẽ xóa tất cả các dòng bắt đầu bằng <header>...bắt đầu từ dòng 3, vì vậy tiêu đề đầu tiên được giữ nguyên và các tiêu đề khác được loại bỏ. Nếu có một số dòng khác nhau trong tiêu đề, hãy điều chỉnh lệnh cho phù hợp (ví dụ: sử dụng tiêu đề 6 dòng 7thay vì 3).
Nếu số lượng dòng trong tiêu đề không xác định, bạn có thể thử như thế này:

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

0

mảng = (* .txt); head -1 $ {mảng [0]}> all.txt; đuôi -n +2 -q $ {mảng [@]: 0} >> all.txt

Giả sử bạn đang sử dụng một thư mục có tệp .txt có cùng tiêu đề cần được kết hợp / nối, mã này sẽ kết hợp tất cả các tệp txt vào all.txt chỉ với một tiêu đề. dòng đầu tiên (các dòng được phân tách bằng dấu chấm phẩy) tập hợp tất cả các tệp văn bản để nối, dòng thứ hai xuất tiêu đề từ tệp txt đầu tiên thành all.txt và dòng cuối cùng nối tất cả các tệp văn bản được thu thập mà không có tiêu đề (bằng cách bắt đầu nối từ hàng 2 trở đi) và nối nó vào all.txt .


một chút giải thích sẽ đi một chặng đường dài hướng tới việc giúp đỡ người dùng trong tương lai
Jeff Schaller
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.