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 XYZ
sao cho file1
có chứa các dòng lên đến XYZ
và phần còn lại của các dòng trong file2
.
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 XYZ
sao cho file1
có chứa các dòng lên đến XYZ
và phần còn lại của các dòng trong file2
.
Câu trả lời:
Với awk
bạn có thể làm:
awk '{print >out}; /XYZ/{out="file2"}' out=file1 largefile
Giải thích: Đối awk
số đầ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 awk
chươ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:
Đây là một công việc cho csplit
:
csplit -sf file -n 1 large_file /XYZ/
sẽ s
phâ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 f
ix file
và được n
đánh số bằng một chữ số duy nhất, file0
v.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 và bao gồm cả khớp dòng, regex
thêm phần +1
bù:
csplit -sf file -n 1 large_file /XYZ/+1
Điều này tạo ra hai tập tin, file0
và file1
. Nếu bạn thực sự cần đặt tên chúng file1
và file2
bạn luôn có thể thêm một mẫu trống vào csplit
lệnh và xóa tệp đầu tiên:
csplit -sf file -n 1 large_file // /XYZ/+1
tạo ra file0
, file1
và file2
nhưng file0
là trống để bạn có thể loại bỏ nó một cách an toàn:
rm -f file0
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 sed
câ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 ksh
một mình (tức là cũng bỏ qua cat
):
{ read in <##XYZ ; print "$in" ; { read <##"" ;} >file2 ;} <largefile >file1
( ksh
Giả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
/ cat
dựa trên).
read
và print
- 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 ksh
nội dung được biên dịch trong - thực sự kỳ lạ đối với tôi đó sed
không phải là một trong số chúng. Nhưng với những thứ như while <file do
tôi đoán bạn không cần sed
nhiều như vậy ...
awk
thực hiện trong điểm chuẩn của bạn? Và mặc dù tôi khá chắc chắn ksh
sẽ 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ì sed
bạn không công bằng lắm sed
- -u
nbuffered 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ì sed
phải làm là lseek mô tả khi kết thúc. Vì lý do gì GNU đảo ngược tâm lý đó.
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 while
vò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).
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
.
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 f
và hai tệp đầu ra f1
và f2
:
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
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:
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
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
XYZ
dòng vào đầu ra hay không?