Lưu ý rằng vấn đề không phải là có tail
nhưng với head
ở đây mà đọc từ các đường ống hơn so với dòng đầu tiên nó có nghĩa là để đầu ra (vì vậy không có gì để lại cho tail
để đọc).
Và vâng, đó là POSIX phù hợp.
head
được yêu cầu rời khỏi con trỏ trong stdin ngay sau dòng cuối cùng nó có đầu ra khi đầu vào có thể tìm kiếm được, nhưng không thì khác.
http://pub.opengroup.org/onlinepub/9699919799/utilities/V3_chap01.html :
Khi tiện ích tiêu chuẩn đọc tệp đầu vào có thể tìm kiếm và chấm dứt mà không có lỗi trước khi đến cuối tệp, tiện ích phải đảm bảo rằng tệp bù trong mô tả tệp mở được đặt đúng vị trí vừa qua byte cuối được xử lý bởi tiện ích. Đối với các tệp không thể tìm kiếm, trạng thái của tệp bù trong mô tả tệp mở cho tệp đó là không xác định.
Để head
có thể làm điều đó đối với một tệp không thể tìm kiếm có nghĩa là nó sẽ phải đọc một byte tại một thời điểm sẽ không hiệu quả khủng khiếp¹. Đó là những gì read
hoặc line
tiện ích làm hoặc GNU sed
với -u
tùy chọn.
Vì vậy, bạn có thể thay thế head -n 20
bằng gsed -u 20q
nếu bạn muốn hành vi đó.
Mặc dù ở đây, bạn muốn:
sed -e 1b -e '$b' -e d
thay thế. Ở đây, chỉ có một lệnh gọi công cụ, do đó, không có vấn đề gì với bộ đệm bên trong không thể chia sẻ giữa hai lần gọi công cụ. Tuy nhiên, xin lưu ý rằng đối với các tệp lớn, sẽ kém hiệu quả hơn khi sed
đọc toàn bộ tệp, trong khi đối với các tệp tail
có thể tìm kiếm sẽ bỏ qua phần lớn tệp bằng cách tìm kiếm ở gần cuối tệp.
Xem các cuộc thảo luận liên quan về bộ đệm tại Tại sao sử dụng vòng lặp shell để xử lý văn bản được coi là thực tiễn xấu? .
Lưu ý rằng tail
phải xuất đuôi của luồng trên stdin. Trong khi, để tối ưu hóa và cho các tệp có thể tìm kiếm, việc triển khai có thể tìm đến cuối tệp để lấy dữ liệu ở đó, không được phép quay lại điểm trước khi vị trí ban đầu tail
được gọi ( Busybox tail
đã từng có lỗi đó).
Vì vậy, ví dụ trong:
{ cat; tail -n 1; } < file
Mặc dù tail
có thể tìm kiếm trở lại dòng cuối cùng file
, nhưng không. Stdin của nó là một luồng trống khi cat
để lại con trỏ ở cuối tệp; không được phép lấy lại dữ liệu từ luồng đó bằng cách tìm kiếm ngược trong tệp.
(Văn bản ở trên bỏ qua việc làm rõ đang chờ xử lý của Nhóm mở và xem xét rằng nó không được thực hiện chính xác bởi một số triển khai)
Built Phần head
dựng sẵn của ksh93
(được bật nếu bạn đặt /opt/ast/bin
trước $PATH
), cho ổ cắm (một loại tệp không thể tìm kiếm) thay vì nhìn trộm vào đầu vào (sử dụng recvfrom(..., MSG_PEEK)
) trước khi thực sự đọc nó để xem nó cần đọc bao nhiêu để đảm bảo rằng nó không cần đọc 'Đọc quá nhiều. Và quay lại đọc một byte mỗi lần cho các loại tệp khác. Đó là một chút hiệu quả hơn và tôi tin là lý do chính tại sao nó thực hiện các đường ống của nó với socketpair()
s thay vì pipe()
. Lưu ý rằng đó không phải là bằng chứng hoàn toàn ngu ngốc vì có một điều kiện chủng tộc có thể được kích hoạt nếu một quá trình khác đọc từ ổ cắm ở giữa nhìn trộm và đọc .
tail
lẽ không đặc biệt hữu ích và thực sự có thể giúp bạn làm việc nhiều hơn dưới mui xe. như stéphane đề cập, nó yêu cầu xác thực đầu vào bổ sung cho mộttail
cách đơn giản hơn là có thể tìm kiếm đến hết đầu vào vì nó phải so sánh bù đầu vào đó với đầu vàolseek()
và kết quả không khác gìhead -n1 file; tail -n1 file
. tôi thấy những thứ đó hữu ích hơn khi cắt đầu vào ra khỏi đầu:while IFS= read -r v; do { printf %s\\n "$v"; head; } >&"$((1+(x=!x)))"; done <in >out1 2>out2