chia tập tin thành hai phần, tại một mẫu


14

Làm thế nào để chia một tập tin lớn thành hai phần, tại một mẫu?

Cho một ví dụ file.txt:

ABC
EFG
XYZ
HIJ
KNL

Tôi muốn tách tệp này XYZsao cho file1có chứa các dòng lên đến XYZvà phần còn lại của các dòng trong file2.


Có nên đưa XYZdòng vào đầu ra hay không?
terdon

@terdon Trong trường hợp của tôi, không có dòng "XYZ" nào không phải là một phần của tệp2. Nhưng nếu bạn có một cách để làm điều đó xin vui lòng thêm để trả lời. Nó có thể hữu ích trong một số trường hợp khác.
d.putto

Đủ công bằng, xong.
terdon

Câu trả lời:


10

Với awkbạn có thể làm:

awk '{print >out}; /XYZ/{out="file2"}' out=file1 largefile


Giải thích: Đối awksố đầu tiên ( out=file1) xác định một biến có tên tệp sẽ được sử dụng cho đầu ra trong khi đối số tiếp theo ( largefile) được xử lý. Các awkchương trình sẽ in tất cả các dòng vào một file nào bởi biến out( {print >out}). Nếu mẫu XYZđược tìm thấy, biến đầu ra sẽ được xác định lại để trỏ đến tệp mới ( {out="file2}") sẽ được sử dụng làm mục tiêu để in các dòng dữ liệu tiếp theo.

Người giới thiệu:


14

Đây là một công việc cho csplit:

csplit -sf file -n 1 large_file /XYZ/

sẽ sphân chia tập tin một cách bất hợp pháp, tạo ra các phần có trước fix filevà được nđánh số bằng một chữ số duy nhất, file0v.v. Lưu ý rằng việc sử dụng /regex/sẽ chia ra, nhưng không bao gồm dòng phù hợp regex. Để phân tách tối đa bao gồm cả khớp dòng, regexthêm phần +1bù:

csplit -sf file -n 1 large_file /XYZ/+1

Điều này tạo ra hai tập tin, file0file1. Nếu bạn thực sự cần đặt tên chúng file1file2bạn luôn có thể thêm một mẫu trống vào csplitlệnh và xóa tệp đầu tiên:

csplit -sf file -n 1 large_file // /XYZ/+1

tạo ra file0, file1file2nhưng file0là trống để bạn có thể loại bỏ nó một cách an toàn:

rm -f file0

Điều này, tôi nghĩ, là câu trả lời đơn giản nhất. Tất cả bạn phải làm là liệt kê một số mẫu và tệp sẽ được phân chia theo thứ tự. Xuất sắc!
Henry Blyth

6

Với một hiện đại, kshđây là một biến thể vỏ (tức là không có sed) của một trong những sedcâu trả lời dựa trên:

{ read in <##XYZ ; print "$in" ; cat >file2 ;} <largefile >file1


Và một biến thể khác trong kshmột mình (tức là cũng bỏ qua cat):

{ read in <##XYZ ; print "$in" ; { read <##"" ;} >file2 ;} <largefile >file1


( kshGiải pháp thuần túy dường như khá hiệu quả; trên tệp thử nghiệm 2,4 GB, nó cần 19-21 giây, so với 39-47 giây với phương pháp sed/ catdựa trên).


rất nhanh. Nhưng tôi không nghĩ rằng bạn cần phải readprint- bạn chỉ nên để nó tự xuất ra. Hiệu suất sẽ tốt hơn nếu bạn xây dựng bộ công cụ AST hoàn toàn và có được tất cả các kshnội dung được biên dịch trong - thực sự kỳ lạ đối với tôi đó sedkhông phải là một trong số chúng. Nhưng với những thứ như while <file dotôi đoán bạn không cần sednhiều như vậy ...
mikeerv

Tôi tò mò mặc dù - làm thế nào đã awkthực hiện trong điểm chuẩn của bạn? Và mặc dù tôi khá chắc chắn kshsẽ luôn chiến thắng trong cuộc chiến này, nhưng nếu bạn đang sử dụng GNU thì sedbạn không công bằng lắm sed- -unbuffered của GNU là một cách tiếp cận kém cỏi đối với POSIXLY để đảm bảo phần bù của bộ mô tả bị bỏ lại khi chương trình thoát khỏi nó - không cần phải làm chậm hoạt động thường xuyên của chương trình - bộ đệm vẫn ổn - tất cả những gì sedphải làm là lseek mô tả khi kết thúc. Vì lý do gì GNU đảo ngược tâm lý đó.
mikeerv

@mikeerv; Khớp mẫu chuyển hướng được thực hiện cho đến khi tìm thấy mẫu và dòng có mẫu tìm thấy sẽ không được in nếu không được thực hiện rõ ràng như mô tả. (Ít nhất điều đó cho thấy thử nghiệm của tôi.) Lưu ý rằng không cówhile ; việc in ấn được thực hiện hoàn toàn như là hiệu ứng phụ được xác định của <##toán tử chuyển hướng. Và chỉ có dòng phù hợp cần in. (Bằng cách đó, việc triển khai tính năng shell là linh hoạt nhất để hỗ trợ inc./excl.) Một whilevòng lặp rõ ràng mà tôi mong đợi sẽ chậm hơn đáng kể (nhưng chưa được kiểm tra).
Janis

1
@mikeerv; À được rồi. BTW, tôi vừa thửhead thay vì read; có vẻ như chỉ chậm hơn một chút, nhưng đó là mã terser : { head -1 <##XYZ ; { read <##"" ;} >file4 ;} <largefile >file3.
Janis

1
@mikeerv; Điểm tốt; không phải vậy Nhưng khi tôi kích hoạt nội dung (chỉ cần thực hiện và kiểm tra kết quả) thì đó là những con số tương tự. (Có thể một số chức năng gọi qua đầu so với đọc?)
Janis

6
{ sed '/XYZ/q' >file1; cat >file2; } <infile

Với GNU, sedbạn nên sử dụng -uchuyển đổi nbuffered. Hầu hết các seds khác chỉ nên làm việc.

Để XYZ ra ...

{ sed -n '/XYZ/q;p'; cat >file2; } <infile >file1

3

Hãy thử điều này với GNU sed:

sed -n -e '1,/XYZ/w file1' -e '/XYZ/,${/XYZ/d;w file2' -e '}' large_file

Ngắn sed -e '1,/XYZ/{w file1' -e 'd}' large_file > file2
gọn

1

Một cách dễ dàng là in ra STDOUT hoặc STDERR, tùy thuộc vào việc mẫu đích có được khớp hay không. Sau đó, bạn có thể sử dụng các toán tử chuyển hướng của shell để chuyển hướng đầu ra tương ứng. Ví dụ: trong Perl, giả sử tệp đầu vào được gọi fvà hai tệp đầu ra f1f2:

  1. Loại bỏ dòng phù hợp với mẫu phân chia:

    perl -ne 'if(/XYZ/){$a=1; next} ; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2
  2. Bao gồm dòng khớp:

    perl -ne '$a=1 if /XYZ/; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2

Ngoài ra, in ra các tệp xử lý khác nhau:

  1. Loại bỏ dòng phù hợp với mẫu phân chia:

    perl -ne 'BEGIN{open($fh1,">","f1");open($fh2,">","f2");}
    if(/XYZ/){$a=1; next}$a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
  2. Bao gồm dòng khớp:

    perl -ne 'BEGIN{open($fh1,">","f1"); open($fh2,">","f2");}
              $a=1 if /XYZ/; $a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
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.