Làm thế nào để có được hành vi nghịch đảo cho `tail` và` head`?


55

Có cách nào để head/ tailmột tài liệu và nhận đầu ra ngược lại; bởi vì bạn không biết có bao nhiêu dòng trong một tài liệu?

Tức là tôi chỉ muốn nhận mọi thứ trừ 2 dòng đầu tiên foo.txtđể nối vào tài liệu khác.

Câu trả lời:


57

Bạn có thể sử dụng điều này để tước hai dòng đầu tiên :

tail -n +3 foo.txt

và điều này để loại bỏ hai dòng cuối cùng :

head -n -2 foo.txt

(giả sử tập tin kết thúc bằng \ncái sau)


Cũng giống như việc sử dụng tiêu chuẩn tailheadcác hoạt động này không phá hủy. Sử dụng >out.txtnếu bạn muốn chuyển hướng đầu ra sang một số tệp mới:

tail -n +3 foo.txt >out.txt

Trong trường hợp out.txtđã tồn tại, nó sẽ ghi đè lên tệp này. Sử dụng >>out.txtthay vì >out.txtnếu bạn muốn có đầu ra được nối vào out.txt.


3
re "head, khi tập tin kết thúc bằng \n" .. Nó hoạt động cho tất cả các số nguyên âm -n -0không trả về gì cả , giống như -n 0(sử dụng: head (GNU coreutils) 7.4) ... Tuy nhiên, khi \nxuất hiện dấu vết , -n -0sẽ in như có thể được mong đợi từ -, tức là. nó in toàn bộ tập tin ... Vì vậy, nó hoạt động với tất cả các giá trị âm khác không .. nhưng -0không thành công khi không có dấu vết\n
Peter.O

@fred: Thực sự lạ lùng (giống với 8.12 ở đây).
Stéphane Gimenez

Là hoạt động này phá hoại? Như tôi muốn nó sao chép nghịch đảo của hai dòng đầu tiên của tài liệu này sang dòng khác?
chrisjlee

@Chris: Không, họ chỉ in kết quả trên "đầu ra tiêu chuẩn", thường được kết nối với thiết bị đầu cuối. Tôi đã thêm một số chi tiết về cách chuyển hướng đầu ra sang một số tệp.
Stéphane Gimenez

7
head -n -2không tương thích POSIX .
l0b0

9

Nếu bạn muốn tất cả trừ các dòng N-1 đầu tiên, hãy gọi tailvới số lượng dòng +N. (Số này là số của dòng đầu tiên bạn muốn giữ lại, bắt đầu từ 1, tức là +1 có nghĩa là bắt đầu ở trên cùng, +2 có nghĩa là bỏ qua một dòng và cứ thế).

tail -n +3 foo.txt >>other-document

Không có cách dễ dàng, di động để bỏ qua N dòng cuối cùng. GNU headcho phép head -n +Nnhư là một đối tác của tail -n +N. Mặt khác, nếu bạn có tac(ví dụ GNU hoặc Busybox), bạn có thể kết hợp nó với đuôi:

tac | tail -n +3 | tac

Có thể sử dụng bộ lọc awk (chưa được kiểm tra):

awk -vskip=2 '{
    lines[NR] = $0;
    if (NR > skip) print lines[NR-skip];
    delete lines[NR-skip];
}'

Nếu bạn muốn xóa một vài dòng cuối cùng khỏi một tệp lớn, bạn có thể xác định phần bù byte của đoạn để cắt bớt sau đó thực hiện cắt ngắn bằng dd.

total=$(wc -c < /file/to/truncate)
chop=$(tail -n 42 /file/to/truncate | wc -c)
dd if=/dev/null of=/file/to/truncate seek=1 bs="$((total-chop))"

Bạn không thể cắt một tệp tại chỗ ngay từ đầu, mặc dù nếu bạn cần xóa một vài dòng đầu tiên của một tệp lớn, bạn có thể di chuyển nội dung xung quanh .


Trên một số hệ thống (như Linux hiện đại), bạn có thể cắt bớt (thu gọn) một tệp tại chỗ ngay từ đầu, nhưng thường chỉ bằng một lượng nhiều kích thước khối FS (vì vậy không thực sự hữu ích trong trường hợp này).
Stéphane Chazelas

3

Từ tailtrang man (GNU tail, nghĩa là):

-n, --lines=K
   output the last K lines, instead of the last 10; or use -n +K to
   output lines starting with the Kth

Do đó, phần sau đây sẽ nối thêm tất cả trừ 2 dòng đầu tiên somefile.txtvào anotherfile.txt:

tail --lines=+3 somefile.txt >> anotherfile.txt

3

Để loại bỏ n dòng GNU sed đầu tiên có thể được sử dụng. Ví dụ: nếu n = 2

sed -n '1,2!p' input-file

!nghĩa là "loại trừ khoảng thời gian này". Như bạn có thể tưởng tượng, ví dụ phức tạp hơn có thể thu được

sed -n '3,5p;7p'

Điều đó sẽ hiển thị dòng 3,4,5,7. Nhiều sức mạnh hơn đến từ việc sử dụng các biểu thức thông thường thay vì địa chỉ.

Hạn chế là số dòng phải được biết trước.


1
Tại sao không chỉ sed 1,2d? Đơn giản hơn thường tốt hơn. Ngoài ra, không có gì trong các ví dụ của bạn là dành riêng cho GNU Sed; tất cả các lệnh của bạn đều sử dụng các tính năng POSIX tiêu chuẩn của Sed .
tự đại diện

1

Bạn có thể sử dụng diffđể so sánh đầu ra của head/ tailvới tệp gốc và sau đó loại bỏ những gì giống nhau, do đó nhận được nghịch đảo.

diff --unchanged-group-format='' foo.txt <(head -2 foo.txt)

1

Trong khi tail -n +4để xuất tệp bắt đầu từ dòng thứ 4 (tất cả trừ 3 dòng đầu tiên) là tiêu chuẩn và di động, thì headđối tác của nó ( head -n -3, tất cả trừ 3 dòng cuối cùng) thì không.

Có thể, bạn sẽ làm:

sed '$d' | sed '$d' | sed '$d'

Hoặc là:

sed -ne :1 -e '1,3{N;b1' -e '}' -e 'P;N;D'

(lưu ý rằng trên một số hệ thống sedcó không gian mẫu có kích thước giới hạn, sẽ không mở rộng thành các giá trị lớn n).

Hoặc là:

awk 'NR>3 {print l[NR%3]}; {l[NR%3]=$0}'

1
{   head -n2 >/dev/null
    cat  >> other_document
}   <infile

Nếu <infilelà một lseek()tập tin thông thường, có thể , thì có, bằng mọi cách, hãy thoải mái. Trên đây là một cấu trúc được hỗ trợ đầy đủ POSIXly.


0

Cách tiếp cận của tôi tương tự như Gilles nhưng thay vào đó tôi chỉ đảo ngược tệp bằng cat và pipe bằng lệnh head.

tac -r thefile.txt | đầu thisfile.txt (thay thế tệp)


0

Hy vọng tôi hiểu rõ nhu cầu của bạn.

Bạn đã có một số cách để hoàn thành yêu cầu của bạn:

tail -n$(expr $(cat /etc/passwd|wc -l) - 2) /etc/passwd

Trong đó / etc / passwd là tệp của bạn

Giải pháp thứ 2 có thể hữu ích nếu bạn có tệp lớn:

my1stline=$(head -n1 /etc/passwd)
my2ndline=$(head -n2 /etc/passwd|grep -v "$my1stline")
cat /etc/passwd |grep -Ev "$my1stline|$my2ndline"

0

Giải pháp cho BSD (macOS):

Xóa 2 dòng đầu tiên:

tail -n $( echo "$(cat foo.txt | wc -l)-2" | bc )

Xóa 2 dòng cuối cùng:

head -n $( echo "$(cat foo.txt | wc -l)-2" | bc )

... không thanh lịch lắm nhưng hoàn thành công việc!

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.