it-swarm-vi.com

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

Có cách nào để head/tail mộ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?

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

57
chrisjlee

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

tail -n +3 foo.txt

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

head -n -2 foo.txt

(giả sử tệp kết thúc bằng \n cho sau này)


Giống như cách sử dụng tiêu chuẩn của tailhead các thao tác này không mang tính hủy diệt. Sử dụng >out.txt nế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.txt thay vì >out.txt nếu bạn muốn có đầu ra được nối vào out.txt.

59
Stéphane Gimenez

Nếu bạn muốn tất cả trừ các dòng N-1 đầu tiên, hãy gọi tail vớ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 head cho phép head -n +N là đố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 với 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 .

Từ trang man tail (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 sẽ nối thêm tất cả trừ 2 dòng đầu tiên của somefile.txt đến anotherfile.txt:

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

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

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

Các ! có nghĩa là "loại trừ khoảng 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.

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

Nếu <infile là một tệp thông thường, lseek() - có thể, thì 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.

2
mikeserv

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à chuẩn và di động, đối tác head 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 có sed có không gian mẫu có kích thước giới hạn, không chia tỷ lệ thành các giá trị lớn của n).

Hoặc là:

awk 'NR>3 {print l[NR%3]}; {l[NR%3]=$0}'
1
Stéphane Chazelas

Bạn có thể sử dụng diff để so sánh đầu ra của head/tail vớ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
sokoban

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
Abe

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!

0
spectrum

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
Mathieu Coavoux