tra-loi-cau-hoi-phat-trien-web.com

unix - đầu VÀ đuôi của tập tin

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. 

109
toop

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.

176
Aleksandra Zalcman

edstandard text editor

$ echo -e '1+10,$-10d\n%p' | ed -s file.txt
17
kev

Đố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;'
10
RantingNerd

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.

3
mah
(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ỏ.

2
guest

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:

  • đầu ra trực tiếp cho đầu (rõ ràng là cho đuôi là không thể)
  • không sử dụng các tập tin bên ngoài
  • thanh tiến trình một dấu chấm cho mỗi dòng sau MAX_LINES, rất hữu ích cho các tác vụ chạy dài.
  • thanh tiến trình trên thiết bị xuất chuẩn, đảm bảo rằng các dấu chấm tiến trình được tách ra khỏi đầu + đuôi (rất tiện dụng nếu bạn muốn đặt ống tiêu chuẩn)
  • tránh thứ tự ghi nhật ký không chính xác có thể do bộ đệm (stdbuf)
  • tránh trùng lặp đầu ra khi tổng số dòng nhỏ hơn đầu + đuôi.
2
sorin

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
2
Samus_

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

1
Paul

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.

1
Gary van der Merwe

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.

1
S.R.I

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 
1
modular

Để 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.

0
Camille Goudeseune

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
0
Jitsusama
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 đủ .

0
mark_infinite

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
0
Script Wolf

Để 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

0
mariana.ft

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
0
zzapper

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] }' ; }
0
Michael Blahay

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

0
lik