Tôi cần phải liên tục xóa dòng đầu tiên khỏi một tệp văn bản lớn bằng cách sử dụng tập lệnh bash.
Ngay bây giờ tôi đang sử dụng sed -i -e "1d" $FILE- nhưng phải mất khoảng một phút để xóa.
Có cách nào hiệu quả hơn để thực hiện điều này?
Tôi cần phải liên tục xóa dòng đầu tiên khỏi một tệp văn bản lớn bằng cách sử dụng tập lệnh bash.
Ngay bây giờ tôi đang sử dụng sed -i -e "1d" $FILE- nhưng phải mất khoảng một phút để xóa.
Có cách nào hiệu quả hơn để thực hiện điều này?
Câu trả lời:
Thử đuôi :
tail -n +2 "$FILE"
-n x: Chỉ cần in những xdòng cuối cùng . tail -n 5sẽ cung cấp cho bạn 5 dòng cuối cùng của đầu vào. Các +loại dấu hiệu đảo ngược đối số và thực hiện tailin bất cứ điều gì ngoại trừ các x-1dòng đầu tiên . tail -n +1sẽ in toàn bộ tập tin, tail -n +2mọi thứ trừ dòng đầu tiên, v.v.
GNU tailnhanh hơn nhiều sed. tailcũng có sẵn trên BSD và -n +2cờ phù hợp trên cả hai công cụ. Kiểm tra các trang man FreeBSD hoặc OS X để biết thêm.
Phiên bản BSD có thể chậm hơn nhiều sed, mặc dù. Tôi tự hỏi làm thế nào họ quản lý điều đó; tailchỉ nên đọc từng dòng tệp trong khi sedthực hiện các thao tác khá phức tạp liên quan đến việc diễn giải một tập lệnh, áp dụng các biểu thức thông thường và tương tự.
Lưu ý: Bạn có thể bị cám dỗ để sử dụng
# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"
nhưng điều này sẽ cung cấp cho bạn một tập tin trống . Lý do là việc chuyển hướng ( >) xảy ra trước đó tailđược gọi bởi shell:
$FILEtailtailquá trình để$FILEtail đọc từ bây giờ trống rỗng $FILENếu bạn muốn xóa dòng đầu tiên bên trong tệp, bạn nên sử dụng:
tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"
Các &&sẽ đảm bảo rằng các tập tin không bị ghi đè khi có một vấn đề.
-rtùy chọn. Có lẽ có một thiết lập bộ đệm ở đâu đó trong hệ thống? Hoặc -nlà một số có chữ ký 32 bit?
tailsẽ làm việc cho bất kỳ kích thước tệp.
-n N means output the last N lines, instead of the last 10; or use +N to output lines starting with the Nth
Bạn có thể sử dụng -i để cập nhật tệp mà không cần sử dụng toán tử '>'. Lệnh sau sẽ xóa dòng đầu tiên khỏi tệp và lưu nó vào tệp.
sed -i '1d' filename
unterminated transform source string
sed -i '1,2d' filename
tail -n +2. Không chắc chắn tại sao nó không phải là câu trả lời hàng đầu.
Đối với những người dùng SunOS không phải là GNU, đoạn mã sau sẽ giúp:
sed '1d' test.dat > tmp.dat
Không, đó là về hiệu quả như bạn sẽ nhận được. Bạn có thể viết chương trình C có thể thực hiện công việc nhanh hơn một chút (thời gian khởi động và xử lý đối số ít hơn) nhưng nó có thể sẽ có xu hướng với tốc độ tương tự như sed khi các tệp trở nên lớn (và tôi cho rằng chúng lớn nếu mất một phút ).
Nhưng câu hỏi của bạn gặp phải vấn đề tương tự như rất nhiều người khác ở chỗ nó đưa ra giải pháp trước. Nếu bạn muốn nói với chúng tôi chi tiết những gì bạn đang cố gắng thực hiện thì làm thế nào , chúng tôi có thể đề xuất một lựa chọn tốt hơn.
Ví dụ: nếu đây là tệp A mà một số chương trình B khác xử lý, một giải pháp sẽ là không loại bỏ dòng đầu tiên, nhưng sửa đổi chương trình B để xử lý nó theo cách khác.
Giả sử tất cả các chương trình của bạn nối vào tệp A và chương trình B hiện đang đọc và xử lý dòng đầu tiên trước khi xóa nó.
Bạn có thể thiết kế lại chương trình B để nó không cố xóa dòng đầu tiên nhưng vẫn duy trì phần bù (có thể dựa trên tệp) liên tục vào tệp A để lần sau chạy, nó có thể tìm cách bù đó, xử lý dòng ở đó, và cập nhật phần bù.
Sau đó, tại một thời điểm yên tĩnh (nửa đêm?), Nó có thể xử lý đặc biệt tệp A để xóa tất cả các dòng hiện đang xử lý và đặt giá trị bù về 0.
Nó chắc chắn sẽ nhanh hơn cho một chương trình để mở và tìm kiếm một tệp chứ không phải mở và viết lại. Thảo luận này giả định rằng bạn có quyền kiểm soát chương trình B, tất nhiên. Tôi không biết nếu đó là trường hợp nhưng có thể có các giải pháp khả thi khác nếu bạn cung cấp thêm thông tin.
awk FNR-1 *.csvcó lẽ nhanh hơn.
Bạn có thể chỉnh sửa các tệp tại chỗ: Chỉ cần sử dụng -icờ của perl , như thế này:
perl -ni -e 'print unless $. == 1' filename.txt
Điều này làm cho dòng đầu tiên biến mất, như bạn yêu cầu. Perl sẽ cần đọc và sao chép toàn bộ tệp, nhưng nó sắp xếp để đầu ra được lưu dưới tên của tệp gốc.
Như Pax đã nói, có lẽ bạn sẽ không nhận được bất kỳ nhanh hơn thế này. Lý do là hầu như không có hệ thống tệp nào hỗ trợ cắt xén từ đầu tệp nên đây sẽ là nthao tác O ( ) trong đó nkích thước của tệp. Những gì bạn có thể làm nhanh hơn nhiều mặc dù ghi đè lên dòng đầu tiên có cùng số byte (có thể có khoảng trắng hoặc nhận xét) có thể phù hợp với bạn tùy thuộc vào chính xác những gì bạn đang cố gắng thực hiện (đó là gì?).
Các spongeutil tránh sự cần thiết cho tung hứng một tập tin temp:
tail -n +2 "$FILE" | sponge "$FILE"
spongethực sự sạch sẽ và mạnh mẽ hơn nhiều so với giải pháp được chấp nhận ( tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE")
spongeđệm toàn bộ tập tin trong bộ nhớ? Điều đó sẽ không hoạt động nếu hàng trăm GB.
spongesẽ ngâm nó lên, vì nó sử dụng tệp / tmp làm bước trung gian, sau đó được sử dụng để thay thế bản gốc sau đó.
Nếu bạn muốn thay đổi các tập tin tại chỗ, bạn luôn có thể sử dụng bản gốc edthay vì nó s kế treaming sed:
ed "$FILE" <<<$'1d\nwq\n'
Các edlệnh là soạn thảo văn bản gốc UNIX, ngay cả trước khi có thiết bị đầu cuối toàn màn hình, máy trạm ít hơn nhiều đồ họa. Các exbiên tập viên, tốt nhất được biết đến như những gì bạn đang sử dụng khi đánh máy tại ruột kết trong cửa sổ vi, là một cựu phiên bản chăm sóc của ed, rất nhiều công việc lệnh tương tự. Mặc dù edcó nghĩa là được sử dụng tương tác, nó cũng có thể được sử dụng trong chế độ hàng loạt bằng cách gửi một chuỗi lệnh đến nó, đó là những gì giải pháp này làm.
Chuỗi <<<$'1d\nwq\n'tận dụng sự hỗ trợ Bash cho đây-strings ( <<<) và dấu ngoặc kép POSIX ( $'... ') để đầu vào thức ăn cho edlệnh bao gồm hai dòng: 1d, mà d eletes dòng 1 , và sau đó wq, trong đó w nghi thức các tập tin trở lại ra đĩa và sau đó q uits phiên chỉnh sửa.
Có thể sử dụng vim để làm điều này:
vim -u NONE +'1d' +'wq!' /tmp/test.txt
Điều này sẽ nhanh hơn, vì vim sẽ không đọc toàn bộ tệp khi xử lý.
+wq!nếu vỏ của bạn là bash. Có lẽ không phải vì !không phải là bắt đầu của một từ, nhưng có được thói quen trích dẫn mọi thứ có lẽ là tốt xung quanh. (Và nếu bạn đang sử dụng siêu hiệu quả bằng cách không trích dẫn một cách không cần thiết, bạn cũng không cần các trích dẫn xung quanh 1d.)
Vì có vẻ như tôi không thể tăng tốc độ xóa, tôi nghĩ rằng một cách tiếp cận tốt có thể là xử lý tệp theo lô như thế này:
While file1 not empty
file2 = head -n1000 file1
process file2
sed -i -e "1000d" file1
end
Hạn chế của điều này là nếu chương trình bị giết ở giữa (hoặc nếu có một số sql xấu ở đó - làm cho phần "process" bị chết hoặc bị khóa), sẽ có các dòng bị bỏ qua hoặc xử lý hai lần .
(file1 chứa các dòng mã sql)
Việc sử dụng đuôi trên các dòng N-1 và hướng nó vào một tệp, sau đó xóa tệp cũ và đổi tên tệp mới thành tên cũ có thực hiện được công việc không?
Nếu tôi đang làm điều này theo chương trình, tôi sẽ đọc qua tệp và nhớ phần bù tệp, sau khi đọc từng dòng, vì vậy tôi có thể tìm lại vị trí đó để đọc tệp có một dòng ít hơn.