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 x
dòng cuối cùng . tail -n 5
sẽ 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 tail
in bất cứ điều gì ngoại trừ các x-1
dòng đầu tiên . tail -n +1
sẽ in toàn bộ tập tin, tail -n +2
mọi thứ trừ dòng đầu tiên, v.v.
GNU tail
nhanh hơn nhiều sed
. tail
cũng có sẵn trên BSD và -n +2
cờ 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 đó; tail
chỉ nên đọc từng dòng tệp trong khi sed
thự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:
$FILE
tail
tail
quá trình để$FILE
tail
đọc từ bây giờ trống rỗng $FILE
Nế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 đề.
-r
tùy chọn. Có lẽ có một thiết lập bộ đệm ở đâu đó trong hệ thống? Hoặc -n
là một số có chữ ký 32 bit?
tail
sẽ 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 *.csv
có 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 -i
cờ 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à n
thao tác O ( ) trong đó n
kí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 sponge
util tránh sự cần thiết cho tung hứng một tập tin temp:
tail -n +2 "$FILE" | sponge "$FILE"
sponge
thự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.
sponge
sẽ 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 ed
thay vì nó s kế treaming sed
:
ed "$FILE" <<<$'1d\nwq\n'
Các ed
lệ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 ex
biê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ù ed
có 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 ed
lệ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.