Giả sử bạn có tệp txt, lệnh nào để xem đồng thời 10 dòng trên cùng và 10 dòng dưới cùng của tệp?
tức là nếu tệp dài 200 dòng, thì hãy xem các dòng 1-10 và 190-200 trong một lần.
Bạn có thể chỉ cần:
(head; tail) < file.txt
Và nếu bạn cần sử dụng đường ống vì một số lý do thì như thế này:
cat file.txt | (head; tail)
Lưu ý: sẽ in các dòng trùng lặp nếu số dòng trong file.txt nhỏ hơn các dòng đầu mặc định + các dòng đuôi mặc định.
ed
là standard text editor
$ echo -e '1+10,$-10d\n%p' | ed -s file.txt
Đối với một luồng thuần (ví dụ: đầu ra từ một lệnh), bạn có thể sử dụng 'tee' để rẽ nhánh luồng và gửi một luồng tới đầu và một luồng tới đuôi. Điều này yêu cầu sử dụng tính năng '> (danh sách)' của bash (+/dev/fd/N):
( COMMAND | tee /dev/fd/3 | head ) 3> >( tail )
hoặc sử dụng/dev/fd/N (hoặc/dev/stderr) cộng với các chuỗi con với chuyển hướng phức tạp:
( ( seq 1 100 | tee /dev/fd/2 | head 1>&3 ) 2>&1 | tail ) 3>&1
( ( seq 1 100 | tee /dev/stderr | head 1>&3 ) 2>&1 | tail ) 3>&1
(Cả hai thứ này sẽ không hoạt động trong csh hoặc tcsh.)
Đối với một cái gì đó có kiểm soát tốt hơn một chút, bạn có thể sử dụng lệnh Perl này:
COMMAND | Perl -e 'my $size = 10; my @buf = (); while (<>) { print if $. <= $size; Push(@buf, $_); if ( @buf > $size ) { shift(@buf); } } print "------\n"; print @buf;'
head -10 file.txt; tail -10 file.txt
Ngoài ra, bạn sẽ cần phải viết chương trình/kịch bản của riêng bạn.
(sed -u 10q; echo ...; tail) < file.txt
Chỉ là một biến thể khác trong chủ đề (head;tail)
, nhưng tránh vấn đề điền vào bộ đệm ban đầu cho các tệp nhỏ.
Phải mất rất nhiều thời gian để kết thúc với giải pháp này, dường như là giải pháp duy nhất bao gồm tất cả các trường hợp sử dụng (cho đến nay):
command | tee full.log | stdbuf -i0 -o0 -e0 awk -v offset=${MAX_LINES:-200} \
'{
if (NR <= offset) print;
else {
a[NR] = $0;
delete a[NR-offset];
printf "." > "/dev/stderr"
}
}
END {
print "" > "/dev/stderr";
for(i=NR-offset+1 > offset ? NR-offset+1: offset+1 ;i<=NR;i++)
{ print a[i]}
}'
Danh sách tính năng:
vấn đề ở đây là các chương trình định hướng luồng không biết trước độ dài của tệp (vì có thể không có tệp nào, nếu đó là luồng thực).
các công cụ như tail
đệm n dòng cuối cùng nhìn thấy và đợi đến cuối luồng, sau đó in.
nếu bạn muốn thực hiện điều này trong một lệnh duy nhất (và để nó hoạt động với bất kỳ bù nào và không lặp lại các dòng nếu chúng trùng nhau), bạn sẽ phải mô phỏng hành vi này mà tôi đã đề cập.
thử cái này đi
awk -v offset=10 '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { for (i=NR-offset+1; i<=NR; i++) print a[i] }' yourfile
10 dòng đầu tiên của file.ext, sau đó là 10 dòng cuối cùng của nó:
cat file.ext | head -10 && cat file.ext | tail -10
10 dòng cuối cùng của tệp, sau đó là 10 dòng đầu tiên:
cat file.ext | tail -10 && cat file.ext | head -10
Sau đó, bạn cũng có thể dẫn đầu ra ở nơi khác:
(cat file.ext | head -10 && cat file.ext | tail -10 ) | your_program
Tôi đã viết một ứng dụng python đơn giản để làm điều này: https://Gist.github.com/ÿvdm/9970522
Nó xử lý các đường ống (luồng) cũng như các tập tin.
Vâng, bạn luôn có thể xâu chuỗi chúng lại với nhau. Giống như vậy, head fiename_foo && tail filename_foo
. Nếu điều đó là không đủ, bạn có thể tự viết cho mình một hàm bash trong tệp .profile hoặc bất kỳ tệp đăng nhập nào bạn sử dụng:
head_and_tail() {
head $1 && tail $1
}
Và, sau đó gọi nó từ Dấu nhắc Shell của bạn: head_and_tail filename_foo
.
Dựa trên Nhận xét của J.F. Sebastian :
cat file | { tee >(head >&3; cat >/dev/null) | tail; } 3>&1
Bằng cách này, bạn có thể xử lý dòng đầu tiên và phần còn lại khác nhau trong một ống, rất hữu ích khi làm việc với dữ liệu CSV:
{ echo N; seq 3;} | { tee >(head -n1 | sed 's/$/*2/' >&3; cat >/dev/null) | tail -n+2 | awk '{print $1*2}'; } 3>&1
N * 2 2 4 6
Để xử lý các đường ống (luồng) cũng như các tệp, hãy thêm tệp này vào tệp .bashrc hoặc .profile của bạn:
headtail() { awk -v offset="$1" '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { for (i=NR-offset+1; i<=NR; i++) print a[i] }' ; }
Sau đó, bạn không thể chỉ
headtail 10 < file.txt
nhưng cũng
a.out | headtail 10
(Điều này vẫn nối thêm các dòng trống giả khi 10 vượt quá độ dài của đầu vào, không giống như a.out | (head; tail)
cũ đơn giản. Cảm ơn bạn, những người trả lời trước.)
Lưu ý: headtail 10
, không phải headtail -10
.
Tôi sẽ nói rằng tùy thuộc vào kích thước của tập tin, việc chủ động đọc nội dung của nó có thể không được mong muốn. Trong hoàn cảnh đó, tôi nghĩ rằng một số kịch bản Shell đơn giản nên đủ.
Đây là cách gần đây tôi đã xử lý việc này cho một số tệp CSV rất lớn mà tôi đang phân tích:
$ for file in *.csv; do echo "### ${file}" && head ${file} && echo ... && tail ${file} && echo; done
Điều này in ra 10 dòng đầu tiên và 10 dòng cuối cùng của mỗi tệp, đồng thời in ra tên tệp và một số Ellipsis trước và sau.
Đối với một tệp lớn, bạn chỉ cần chạy như sau để có cùng hiệu quả:
$ head somefile.csv && echo ... && tail somefile.csv
sed -n "1,10p; $(( $(wc -l ${aFile} | grep -oE "^[[:digit:]]+")-9 )),\$p" "${aFile}"
NOTE: Biến aFile chứa tệp đường dẫn đầy đủ .
Dựa trên những gì @Samus_ đã giải thích tại đây về cách hoạt động của lệnh @Aleksandra Zalcman, biến thể này rất hữu ích khi bạn không thể nhanh chóng phát hiện ra đuôi bắt đầu mà không đếm các dòng.
{ head; echo "####################\n...\n####################"; tail; } < file.txt
Hoặc nếu bạn bắt đầu làm việc với thứ gì đó ngoài 20 dòng, số lượng dòng thậm chí có thể giúp ích.
{ head -n 18; tail -n 14; } < file.txt | cat -n
Để in 10 dòng đầu tiên và 10 dòng cuối cùng của tệp, bạn có thể thử điều này:
cat <(head -n10 file.txt) <(tail -n10 file.txt) | less
dựa trên ý tưởng trên (bash & zsh đã thử nghiệm)
nhưng sử dụng đầu và đuôi bí danh
alias hat='(head -5 && echo "^^^------vvv" && tail -5) < '
hat large.sql
Tôi đã tìm kiếm giải pháp này trong một thời gian. Đã thử nó với sed, nhưng vấn đề không biết trước về độ dài của tập tin/luồng là không thể vượt qua. Trong tất cả các tùy chọn có sẵn ở trên, tôi thích giải pháp awk của Camille Goudeseune. Anh ta đã lưu ý rằng giải pháp của anh ta để lại các dòng trống thừa ở đầu ra với một bộ dữ liệu đủ nhỏ. Ở đây tôi cung cấp một sửa đổi của giải pháp của mình mà loại bỏ các dòng thêm.
headtail() { awk -v offset="$1" '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { a_count=0; for (i in a) {a_count++}; for (i=NR-a_count+1; i<=NR; i++) print a[i] }' ; }
Tại sao không sử dụng sed
cho nhiệm vụ này?
sed -n -e 1,+9p -e 190,+9p textfile.txt