Cái nào nhanh hơn để xóa dòng đầu tiên trong tập tin sed sed hay tail?


14

Trong câu trả lời này ( Làm cách nào tôi có thể xóa dòng đầu tiên của tệp bằng sed? ) Có hai cách để xóa bản ghi đầu tiên trong một tệp:

sed '1d' $file >> headerless.txt

** ---------------- HOẶC LÀ ----------------**

tail -n +2 $file >> headerless.txt

Cá nhân tôi nghĩ rằng tailtùy chọn này là dễ chịu hơn và dễ đọc hơn nhưng có lẽ vì tôi bị thách thức.

Phương pháp nào nhanh nhất?


5
Không phải là một câu trả lời, nhưng một sự cân nhắc khả thi là có sedtính di động cao hơn: "+2" để tailhoạt động tốt trên Ubuntu, sử dụng GNU tail, nhưng sẽ không hoạt động trên BSD tail.
John N

@ John cảm ơn vì đã chia sẻ tailthiếu khả năng tương thích đa nền tảng.
WinEunuuchs2Unix

3
@John N "+2" cho đuôi hoạt động tốt trên Mac có thể chạy Sierra, người tuyên bố sẽ sử dụng lệnh đuôi BSD
Nick Sillito

Urgh, bạn hoàn toàn đúng - Tôi vừa chạy lại nó và lần này đã kiểm tra đầu vào. Mà tôi nên làm lần đầu tiên. Đó là POSIX, quá. / trượt ra, lúng túng.
John N

2
@ John Bạn không hoàn toàn sai. Trước đây, UNIX không cung cấp -ntùy chọn và sử dụng cú pháp tail +2 $file. Xem freebsd.org/cgi/ Từ Có thể bạn đã nghĩ về điều đó hơn là một trong những BSD hiện đại.
hvd

Câu trả lời:


28

Hiệu suất sedso với tailđể loại bỏ dòng đầu tiên của tệp

TL; DR

  • sed là rất mạnh mẽ và linh hoạt, nhưng đây là những gì làm cho nó chậm, đặc biệt là đối với các tệp lớn có nhiều dòng.

  • tail thực hiện chỉ một điều đơn giản, nhưng đó là một việc tốt và nhanh chóng, ngay cả đối với các tệp lớn hơn có nhiều dòng.

Đối với các tệp có kích thước vừa và nhỏ sedtailđang thực hiện nhanh tương tự (hoặc chậm, tùy thuộc vào mong đợi của bạn). Tuy nhiên, đối với các tệp đầu vào lớn hơn (nhiều MB), sự khác biệt hiệu suất tăng đáng kể (một mức độ lớn cho các tệp trong phạm vi hàng trăm MB), với tailhiệu suất vượt trội rõ ràng sed.

Thí nghiệm

Chuẩn bị chung:

Các lệnh của chúng tôi để phân tích là:

sed '1d' testfile > /dev/null
tail -n +2 testfile > /dev/null

Lưu ý rằng tôi đang dẫn đầu ra cho /dev/nullmỗi lần để loại bỏ đầu ra đầu cuối hoặc tập tin ghi là nút cổ chai hiệu năng.

Chúng ta hãy thiết lập một đĩa RAM để loại bỏ I / O đĩa như một nút cổ chai tiềm năng. Cá nhân tôi đã tmpfsgắn kết /tmpvì vậy tôi chỉ cần đặt testfilenó ở đó cho thí nghiệm này.

Sau đó, tôi đã từng tạo một tệp thử nghiệm ngẫu nhiên chứa một lượng dòng xác định $numoflinesvới độ dài dòng ngẫu nhiên và dữ liệu ngẫu nhiên bằng lệnh này (lưu ý rằng nó chắc chắn không tối ưu, nó trở nên rất chậm đối với khoảng> 2M dòng, nhưng ai quan tâm, nó không phải là điều chúng tôi đang phân tích):

cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n "$numoflines" > testfile

Ồ, btw. máy tính xách tay thử nghiệm của tôi đang chạy Ubuntu 16.04, 64 bit trên CPU Intel i5-6200U. Chỉ để so sánh.

Thời gian tập tin lớn:

Thiết lập rất lớn testfile:

Chạy lệnh trên với numoflines=10000000một tệp ngẫu nhiên chứa 10M dòng, chiếm hơn 600 MB - nó khá lớn, nhưng hãy bắt đầu với nó, bởi vì chúng ta có thể:

$ wc -l testfile 
10000000 testfile

$ du -h testfile 
611M    testfile

$ head -n 3 testfile 
qOWrzWppWJxx0e59o2uuvkrfjQbzos8Z0RWcCQPMGFPueRKqoy1mpgjHcSgtsRXLrZ8S4CU8w6O6pxkKa3JbJD7QNyiHb4o95TSKkdTBYs8uUOCRKPu6BbvG
NklpTCRzUgZK
O/lcQwmJXl1CGr5vQAbpM7TRNkx6XusYrO

Thực hiện chạy theo thời gian với số lượng lớn của chúng tôi testfile:

Bây giờ chúng ta hãy thực hiện một lần chạy theo thời gian duy nhất với cả hai lệnh trước để ước tính mức độ chúng ta đang làm việc.

$ time sed '1d' testfile > /dev/null
real    0m2.104s
user    0m1.944s
sys     0m0.156s

$ time tail -n +2 testfile > /dev/null
real    0m0.181s
user    0m0.044s
sys     0m0.132s

Chúng tôi đã thấy một kết quả thực sự rõ ràng cho các tệp lớn, tailnhanh hơn nhiều so với sed. Nhưng chỉ để cho vui và để chắc chắn rằng không có tác dụng phụ ngẫu nhiên nào tạo ra sự khác biệt lớn, hãy làm điều đó 100 lần:

$ time for i in {1..100}; do sed '1d' testfile > /dev/null; done
real    3m36.756s
user    3m19.756s
sys     0m15.792s

$ time for i in {1..100}; do tail -n +2 testfile > /dev/null; done
real    0m14.573s
user    0m1.876s
sys     0m12.420s

Kết luận vẫn giữ nguyên, sedkhông hiệu quả để loại bỏ dòng đầu tiên của một tệp lớn, tailnên được sử dụng ở đó.

Và vâng, tôi biết các cấu trúc vòng lặp của Bash rất chậm, nhưng chúng tôi chỉ thực hiện tương đối ít lần lặp ở đây và thời gian một vòng lặp đơn giản không đáng kể so với sed/ tailthời gian chạy.

Thời gian tập tin nhỏ:

Thiết lập nhỏ testfile:

Bây giờ để hoàn thiện, chúng ta hãy xem trường hợp phổ biến hơn là bạn có một tệp đầu vào nhỏ trong phạm vi kB. Hãy tạo một tệp đầu vào ngẫu nhiên với numoflines=100, trông như thế này:

$ wc -l testfile 
100 testfile

$ du -h testfile 
8,0K    testfile

$ head -n 3 testfile 
tYMWxhi7GqV0DjWd
pemd0y3NgfBK4G4ho/
aItY/8crld2tZvsU5ly

Thực hiện chạy theo thời gian với nhỏ của chúng tôi testfile:

Như chúng ta có thể mong đợi thời gian cho các tệp nhỏ như vậy nằm trong phạm vi vài mili giây từ kinh nghiệm, hãy thực hiện ngay 1000 lần lặp:

$ time for i in {1..1000}; do sed '1d' testfile > /dev/null; done
real    0m7.811s
user    0m0.412s
sys     0m7.020s

$ time for i in {1..1000}; do tail -n +2 testfile > /dev/null; done
real    0m7.485s
user    0m0.292s
sys     0m6.020s

Như bạn có thể thấy, thời gian khá giống nhau, không có nhiều điều để diễn giải hay thắc mắc. Đối với các tệp nhỏ, cả hai công cụ đều phù hợp như nhau.


+1 để trả lời cảm ơn. Tôi đã chỉnh sửa câu hỏi ban đầu (xin lỗi) dựa trên nhận xét từ Serg awkcũng có thể làm điều này. Câu hỏi ban đầu của tôi được dựa trên liên kết tôi tìm thấy ở nơi đầu tiên. Sau tất cả công việc khó khăn của bạn, xin vui lòng tư vấn nếu tôi nên loại bỏ awknhư một ứng cử viên giải pháp và trở lại tập trung vào phạm vi dự án ban đầu chỉ sedtail.
WinEunuuchs2Unix

Hệ thống này là gì? Trên máy mac của tôi (công cụ BSD), thử nghiệm trên / usr / share / dict / words mang lại cho tôi 0,09 giây cho sed và 0,19 cho đuôi (và awk 'NR > 1', thật thú vị).
Kevin

5

Đây là một lựa chọn khác, chỉ sử dụng các nội dung bash và cat:

{ read ; cat > headerless.txt; } < $file

$fileđược chuyển hướng vào { }nhóm lệnh. Đơn readgiản chỉ cần đọc và loại bỏ dòng đầu tiên. Phần còn lại của luồng sau đó được dẫn tới catghi vào tệp đích.

Trên Ubuntu 16.04 của tôi, hiệu năng của điều này và tailgiải pháp rất giống nhau. Tôi đã tạo một tệp thử nghiệm lớn với seq:

$ seq 100000000 > 100M.txt
$ ls -l 100M.txt 
-rw-rw-r-- 1 ubuntu ubuntu 888888898 Dec 20 17:04 100M.txt
$

tail giải pháp:

$ time tail -n +2 100M.txt > headerless.txt

real    0m1.469s
user    0m0.052s
sys 0m0.784s
$ 

cat/ giải pháp niềng răng:

$ time { read ; cat > headerless.txt; } < 100M.txt 

real    0m1.877s
user    0m0.000s
sys 0m0.736s
$ 

Tôi chỉ có một máy ảo Ubuntu tiện dụng ngay bây giờ và thấy sự thay đổi đáng kể về thời gian của cả hai, mặc dù tất cả chúng đều nằm trong cùng một sân bóng.


1
+1 để trả lời cảm ơn. Đó là một giải pháp rất thú vị và tôi thích niềng răng và đọc từ phải sang trái theo thứ tự phân cấp của bash. (không chắc chắn nếu tôi nói từ đó chính xác). Có thể cập nhật câu trả lời của bạn với kích thước của tệp đầu vào và kết quả điểm chuẩn thời gian nếu điều đó đủ dễ thực hiện không?
WinEunuuchs2Unix

@ WinEunuuchs2Unix Timings được thêm vào, mặc dù chúng không đáng tin cậy lắm vì đây là trên VM. Tôi không có cài đặt Ubuntu bằng kim loại tiện dụng ngay bây giờ.
Chấn thương kỹ thuật số

Tôi không nghĩ VM vs Bare Metal có vấn đề gì khi bạn so sánh VM với VM. Cảm ơn các bằng chứng thời gian. Có lẽ tôi sẽ đi cùng tailnhưng vẫn nghĩ rằng readtùy chọn này rất tuyệt.
WinEunuuchs2Unix

4

Thử hệ thống của tôi và tiền tố mỗi lệnh với timetôi nhận được các kết quả sau:

sed:

real    0m0.129s
user    0m0.012s
sys     0m0.000s

và đuôi:

real    0m0.003s
user    0m0.000s
sys     0m0.000s

trong đó gợi ý rằng, trên hệ thống của tôi ít nhất AMD FX 8250 chạy Ubuntu 16.04, đuôi nhanh hơn đáng kể. Tệp thử nghiệm có 10.000 dòng với kích thước 540k. Các tập tin đã được đọc từ một ổ cứng.


+1 để trả lời cảm ơn. Trong một thử nghiệm riêng biệt trong AU Chatroom, một người dùng cho thấy đuôi nhanh hơn 10 lần (2,31 giây) so với sed (21,86 giây) khi sử dụng RAMDisk với tệp 61 MB. Tôi đã chỉnh sửa câu trả lời của bạn để áp dụng các khối mã nhưng bạn cũng có thể muốn chỉnh sửa nó với kích thước tệp bạn đã sử dụng.
WinEunuuchs2Unix

@Serg Hoàn toàn công bằng rằng đây chỉ là một câu trả lời giai thoại và có khả năng bạn sẽ nhận được kết quả khác nhau với các cấu hình phần cứng khác nhau, các tệp thử nghiệm khác nhau, v.v.
Nick Sillito

2
Tệp không nằm trong bộ đệm, khi sử dụng sedcó thể đóng vai trò trong kết quả này, đó là thứ tự bạn đã kiểm tra chúng.
Minix

hệ thống gì? Như tôi đã nhận xét về một bài đăng khác ở đây, trên mac của tôi sednhanh gấp khoảng hai lần.
Kevin

1

Không có cách khách quan nào để nói cái nào tốt hơn, bởi vì sedtailkhông phải là thứ duy nhất chạy trên hệ thống trong khi thực hiện chương trình. Rất nhiều yếu tố như i / o đĩa, i / o mạng, CPU bị gián đoạn cho các quá trình ưu tiên cao hơn - tất cả những yếu tố này ảnh hưởng đến việc chương trình của bạn sẽ chạy nhanh như thế nào.

Cả hai đều được viết bằng C, vì vậy đây không phải là vấn đề ngôn ngữ, mà là vấn đề môi trường. Ví dụ: tôi có SSD và trên hệ thống của tôi, việc này sẽ mất thời gian tính bằng micro giây, nhưng đối với cùng một tệp trên ổ cứng thì sẽ mất nhiều thời gian hơn vì ổ cứng chậm hơn đáng kể. Vì vậy, phần cứng cũng đóng vai trò trong việc này.

Có một vài điều mà bạn có thể muốn ghi nhớ khi xem xét nên chọn lệnh nào:

  • Mục đích của bạn là gì ? sedlà trình chỉnh sửa luồng để chuyển đổi văn bản. taillà để xuất các dòng cụ thể của văn bản. Nếu bạn muốn xử lý các dòng và chỉ in chúng ra, sử dụng tail. Nếu bạn muốn chỉnh sửa văn bản, sử dụng sed.
  • tailcó cú pháp đơn giản hơn nhiều so với sed, vì vậy hãy sử dụng những gì bạn có thể tự đọc và những gì người khác có thể đọc.

Một yếu tố quan trọng khác là lượng dữ liệu bạn đang xử lý. Các tệp nhỏ sẽ không cung cấp cho bạn bất kỳ sự khác biệt hiệu suất. Hình ảnh trở nên thú vị khi bạn xử lý các tệp lớn. Với BIGFILE.txt 2 GB, chúng ta có thể thấy rằng sedcó nhiều cuộc gọi hệ thống hơn nhiều tailvà chạy chậm hơn đáng kể.

bash-4.3$ du -sh BIGFILE.txt 
2.0G    BIGFILE.txt
bash-4.3$ strace -c  sed '1d' ./BIGFILE.txt  > /dev/null
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 59.38    0.079781           0    517051           read
 40.62    0.054570           0    517042           write
  0.00    0.000000           0        10         1 open
  0.00    0.000000           0        11           close
  0.00    0.000000           0        10           fstat
  0.00    0.000000           0        19           mmap
  0.00    0.000000           0        12           mprotect
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         2           rt_sigaction
  0.00    0.000000           0         1           rt_sigprocmask
  0.00    0.000000           0         1         1 ioctl
  0.00    0.000000           0         7         7 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0         2         2 statfs
  0.00    0.000000           0         1           arch_prctl
  0.00    0.000000           0         1           set_tid_address
  0.00    0.000000           0         1           set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00    0.134351               1034177        11 total
bash-4.3$ strace -c  tail  -n +2 ./BIGFILE.txt  > /dev/null
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 62.30    0.148821           0    517042           write
 37.70    0.090044           0    258525           read
  0.00    0.000000           0         9         3 open
  0.00    0.000000           0         8           close
  0.00    0.000000           0         7           fstat
  0.00    0.000000           0        10           mmap
  0.00    0.000000           0         4           mprotect
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         1         1 ioctl
  0.00    0.000000           0         3         3 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           arch_prctl
------ ----------- ----------- --------- --------- ----------------
100.00    0.238865                775615         7 total

+1 để trả lời cảm ơn. Nhưng tôi không chắc nhận xét này sẽ giúp tôi quyết định nên sử dụng lệnh nào ....
WinEunuuchs2Unix

@ WinEunuuchs2Unix Vâng, bạn đã hỏi lệnh nào tốt hơn, vì vậy tôi đang trả lời chính xác câu hỏi đó. Chọn lệnh nào, tùy bạn. Nếu bạn có thể đọc tailtốt hơn sed- sử dụng nó. Cá nhân tôi sẽ sử dụng pythonhoặc awkhơn là sedbởi vì nó có thể trở nên phức tạp. Ngoài ra, nếu bạn lo lắng về hiệu suất, hãy đối mặt với thực tế - bạn đang thấy kết quả tính bằng micrô giây ở đây. Bạn sẽ không cảm thấy sự khác biệt trừ khi đó là một tập tin khổng lồ trong phạm vi gigabyte mà bạn đang cố đọc
Sergiy Kolodyazhnyy

Ồ tôi cũng sẽ đánh giá cao một awkcâu trả lời :) ... Câu hỏi của tôi được dựa trên một câu hỏi và trả lời khác của AU (trong liên kết) và ở đó họ không bao giờ đề cập đến awk. Tôi đồng ý sự khác biệt thời gian là danh nghĩa trên các tập tin nhỏ. Tôi chỉ cố gắng phát triển một số thói quen tốt.
WinEunuuchs2Unix

1
@ WinEunuuchs2Unix Chắc chắn, đây là : awk 'NR!=1' input_file.txt . Nó cho tôi kết quả như nhau, khoảng 150 mili giây, cùng một số cho cả hai tailsed. Nhưng agian, tôi đang sử dụng SSD, vì vậy tôi muốn nói rằng đó là ổ cứng và CPU mới là vấn đề chứ không phải lệnh.
Sergiy Kolodyazhnyy

1
@Serg thậm chí chỉ với một tệp 60 MB chứa 1M dòng, 1000 lần chạy sedmất hơn 3 phút, trong khi tailchỉ cần khoảng 20 giây. Đó không phải là lớn nhưng trên thực tế, chắc chắn không nằm trong phạm vi GB.
Chỉ huy Byte

1

Câu trả lời hàng đầu đã không đưa đĩa vào tài khoản > /dev/null

nếu bạn có một tệp lớn và không muốn tạo một bản sao tạm thời trên đĩa của mình, hãy thử vim -c

$ cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n 10000000 > testfile
$ time sed -i '1d' testfile

real    0m59.053s
user    0m9.625s
sys     0m48.952s

$ cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n 10000000 > testfile
$ time vim -e -s testfile -c ':1d' -c ':wq'

real    0m8.259s
user    0m3.640s
sys     0m3.093s

Chỉnh sửa: nếu tệp lớn hơn bộ nhớ khả dụng vim -ckhông hoạt động, có vẻ như nó không đủ thông minh để thực hiện tải tăng dần của tệp


0

Các câu trả lời khác cho thấy những gì tốt hơn để tạo một tệp mới với dòng đầu tiên bị thiếu. Nếu bạn muốn chỉnh sửa một tệp trái ngược với việc tạo một tệp mới, tôi cá là edsẽ nhanh hơn vì không nên tạo một tệp mới. Nhưng bạn phải tìm cách xóa một dòng edvì tôi chỉ sử dụng nó một lần.

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.