Xóa dòng cuối cùng khỏi một tệp trong Bash


Câu trả lời:


406

Sử dụng GNU sed:

sed -i '$ d' foo.txt

Các -itùy chọn không tồn tại trong GNU sedcác phiên bản cũ hơn 3,95, vì vậy bạn phải sử dụng nó như một bộ lọc với một tập tin tạm thời:

cp foo.txt foo.txt.tmp
sed '$ d' foo.txt.tmp > foo.txt
rm -f foo.txt.tmp

Tất nhiên, trong trường hợp đó bạn cũng có thể sử dụng head -n -1thay vì sed.

Hệ điều hành Mac:

Trên Mac OS X (kể từ ngày 10.7.4), tương đương với sed -ilệnh trên là

sed -i '' -e '$ d' foo.txt

56
Trên Mac OS X (kể từ ngày 10.7.4), tương đương với sed -ilệnh trên là sed -i '' -e '$ d' foo.txt. Ngoài ra, head -n -1hiện tại sẽ không hoạt động trên máy Mac.
mkuity0

26
Bạn có thể giải thích regex '$ d' không? Đó là câu hỏi về việc loại bỏ dòng cuối cùng, vì vậy tôi nghĩ đây là phần quan trọng nhất cho mọi người xem câu hỏi này. Cảm ơn :)
kravemir

38
@Miro: Không có nghĩa là $ dmột regex. Đó là một lệnh sed. dlà lệnh xóa một dòng, trong khi đó $có nghĩa là "dòng cuối cùng trong tệp". Khi chỉ định một vị trí (được gọi là "phạm vi" trong sed lingo) trước một lệnh, lệnh đó chỉ được áp dụng cho vị trí đã chỉ định. Vì vậy, lệnh này nói rõ ràng "trong phạm vi của dòng cuối cùng trong một tệp, xóa nó". Khá lắt léo và đi thẳng vào vấn đề, nếu bạn hỏi tôi.
Daniel Kamil Kozar

1
Làm thế nào để bạn loại bỏ dòng cuối cùng nhưng chỉ khi đó là một dòng trống?
skan

1
@Alex: được cập nhật cho GNU sed; không biết gì về các phiên bản sed BSD / UNIX / MacOS khác nhau ...
thkala

279

Đây là giải pháp nhanh nhất và đơn giản nhất, đặc biệt là trên các tệp lớn:

head -n -1 foo.txt > temp.txt ; mv temp.txt foo.txt

Nếu bạn muốn xóa dòng trên cùng, hãy sử dụng:

tail -n +2 foo.txt

có nghĩa là các dòng đầu ra bắt đầu từ dòng 2.

Không sử dụng sedđể xóa các dòng từ trên cùng hoặc dưới cùng của tệp - sẽ rất chậm nếu tệp lớn.


16
head -n -1 foo.txtlà đủ
ДМИТРИЙ МАЛИКОВ

20
head -n -1 không hoạt động trên đầu của bdsutils. ít nhất là không có trong phiên bản macos đang sử dụng, vì vậy điều này không hoạt động.
johannes_lalala

23
@johannes_lalala Trên Mac, bạn có thể brew install coreutils(tiện ích lõi GNU) và sử dụng gheadthay vì head.
thú vị khác

Đây là một tập lệnh bash đơn giản tự động hóa nó trong trường hợp bạn có nhiều tệp với số dòng khác nhau ở phía dưới để xóa: cat TAILfixer FILE=$1; TAIL=$2; head -n -${TAIL} ${FILE} > temp ; mv temp ${FILE}Chạy nó để xóa 4 dòng ví dụ từ myfile như: ./TAILfixer myfile 4tất nhiên trước tiên làm cho nó có thể thực thi được bằngchmod +x TAILfixer
FatihSarigol

Đồng ý vì nó hoạt động, nhưng đối với tất cả các so sánh sed, lệnh này cũng khá chậm
Jeff Diederiks

121

Tôi gặp rắc rối với tất cả các câu trả lời ở đây vì tôi đang làm việc với tệp HUGE (~ 300Gb) và không có giải pháp nào được thu nhỏ. Đây là giải pháp của tôi:

dd if=/dev/null of=<filename> bs=1 seek=$(echo $(stat --format=%s <filename> ) - $( tail -n1 <filename> | wc -c) | bc )

Nói cách: Tìm ra độ dài của tệp bạn muốn kết thúc bằng (độ dài của tệp trừ đi độ dài của dòng cuối cùng của nó, bằng cách sử dụng bc) và đặt vị trí đó là cuối của tệp (bằng cách nhập ddmột byte /dev/nullvào nó).

Việc này rất nhanh vì tailbắt đầu đọc từ cuối và ddsẽ ghi đè tệp vào vị trí thay vì sao chép (và phân tích) mọi dòng của tệp, đó là những gì các giải pháp khác làm.

LƯU Ý: Thao tác này sẽ xóa dòng khỏi tệp tại chỗ! Tạo một bản sao lưu hoặc kiểm tra trên một tập tin giả trước khi thử nó trên tập tin của riêng bạn!


3
Tôi thậm chí còn không biết về dd. Đây phải là câu trả lời hàng đầu. Cảm ơn bạn rất nhiều! Thật xấu hổ khi giải pháp không hoạt động đối với (chặn) các tệp được nén.
tommy.carstensen

4
Cách tiếp cận rất, rất thông minh! Chỉ cần một lưu ý: nó yêu cầu và hoạt động trên một tập tin thực, vì vậy nó không thể được sử dụng trên các đường ống, thay thế lệnh, chuyển hướng và các chuỗi như vậy.
MestreLion

3
Đây phải là câu trả lời hàng đầu. Làm việc ngay lập tức cho tập tin ~ 8 GB của tôi.
Peter Cogan

8
người dùng mac: dd if = / dev / null of = <tên tệp> bs = 1 tìm kiếm = $ (echo $ (stat -f =% z <tên tệp> | cut -c 2-) - $ (tail -n1 <tên tệp> | wc -c) | bc)
Igor

2
Cách tiếp cận này là cách để đi cho các tập tin lớn!
thomas.han

61

Để xóa dòng cuối cùng khỏi tệp mà không cần đọc toàn bộ tệp hoặc viết lại bất cứ thứ gì , bạn có thể sử dụng

tail -n 1 "$file" | wc -c | xargs -I {} truncate "$file" -s -{}

Để xóa dòng cuối cùng và cũng in nó trên thiết bị xuất chuẩn ("pop" nó), bạn có thể kết hợp lệnh đó với tee:

tail -n 1 "$file" | tee >(wc -c | xargs -I {} truncate "$file" -s -{})

Các lệnh này có thể xử lý một cách hiệu quả một tệp rất lớn. Điều này tương tự và được lấy cảm hứng từ câu trả lời của Yossi, nhưng nó tránh sử dụng một vài chức năng bổ sung.

Nếu bạn sẽ sử dụng chúng nhiều lần và muốn xử lý lỗi và một số tính năng khác, bạn có thể sử dụng poptaillệnh tại đây: https://github.com/donm/evenmoreutils


3
Ghi chú nhanh: truncatekhông có sẵn trên OS X theo mặc định. Nhưng thật dễ dàng để cài đặt bằng cách sử dụng bia : brew install truncate.
Guillaume Boudreau

3
Rất đẹp. Tốt hơn nhiều mà không có dd. Tôi đã rất sợ sử dụng dd như một chữ số hoặc chữ cái bị thất lạc có thể đánh vần thảm họa .. +1
Yossi Farjoun

1
(CẢNH BÁO CÔNG VIỆC) Trên thực tế, ("dd" -> "thảm họa") yêu cầu 1 lần thay thế và 6 lần chèn; hầu như không "một chữ số hoặc chữ cái bị thất lạc". :)
Kyle

29

Người dùng Mac

nếu bạn chỉ muốn dòng đầu ra bị xóa mà không thay đổi tập tin

sed -e '$ d' foo.txt

nếu bạn muốn xóa dòng cuối cùng của chính tệp đầu vào

sed -i '' -e '$ d' foo.txt


Điều này làm việc cho tôi nhưng tôi không hiểu. dù sao, nó đã làm việc
Melvin

Cờ báo -i ''cho sed sửa đổi tệp tại chỗ, trong khi giữ bản sao lưu trong tệp có phần mở rộng được cung cấp dưới dạng tham số. Vì tham số là một chuỗi rỗng, không có tệp sao lưu nào được tạo. -enói với sed để thực hiện một lệnh. Lệnh này $ dcó nghĩa là: tìm dòng cuối cùng ( $) và xóa nó ( d).
Krzysztof Szafranek ngày

24

Dành cho người dùng Mac:

Trên máy Mac, đầu -n -1 sẽ không hoạt động. Và, tôi đã cố gắng tìm một giải pháp đơn giản [mà không phải lo lắng về thời gian xử lý] để giải quyết vấn đề này chỉ bằng cách sử dụng các lệnh "head" và / hoặc "tail".

Tôi đã thử chuỗi lệnh sau đây và rất vui vì tôi có thể giải quyết nó chỉ bằng cách sử dụng lệnh "tail" [với các tùy chọn có sẵn trên Mac]. Vì vậy, nếu bạn đang ở trên Mac và chỉ muốn sử dụng "đuôi" để giải quyết vấn đề này, bạn có thể sử dụng lệnh này:

tập tin mèo | đuôi -r | đuôi -n +2 | đuôi -r

Giải trình :

1> tail -r: chỉ cần đảo ngược thứ tự các dòng trong đầu vào của nó

2> tail -n +2: điều này in tất cả các dòng bắt đầu từ dòng thứ hai trong đầu vào của nó


2
Cài đặt coreutils bằng Homebrew sẽ cung cấp cho bạn phần đầu GNU dưới dạng 'ghead', cho phép bạn thực hiệnghead -n -1
phần nào từ

13
echo -e '$d\nw\nq'| ed foo.txt

2
Đối với giải pháp bộ lọc không chỉnh sửa tệp tại chỗ, hãy sử dụng sed '$d'.
lhf

8
awk 'NR>1{print buf}{buf = $0}'

Về cơ bản, mã này cho biết như sau:

Đối với mỗi dòng sau đầu tiên, in dòng đệm

cho mỗi dòng, đặt lại bộ đệm

Bộ đệm bị trễ bởi một dòng, do đó bạn kết thúc việc in các dòng từ 1 đến n-1


2
Giải pháp này là một bộ lọc và không chỉnh sửa tập tin tại chỗ.
lhf

0
awk "NR != `wc -l < text.file`" text.file |> text.file

Đoạn trích này thực hiện các mẹo.


Điều này chỉ xóa toàn bộ tập tin cho tôi.
Kartik Chugh

0

Cả hai giải pháp này đều ở đây trong các hình thức khác. Tôi thấy những điều này thực tế hơn một chút, rõ ràng và hữu ích:

Sử dụng dd:

BADLINESCOUNT=1
ORIGINALFILE=/tmp/whatever
dd if=${ORIGINALFILE} of=${ORIGINALFILE}.tmp status=none bs=1 count=$(printf "$(stat --format=%s ${ORIGINALFILE}) - $(tail -n${BADLINESCOUNT} ${ORIGINALFILE} | wc -c)\n" | bc )
/bin/mv -f ${ORIGINALFILE}.tmp ${ORIGINALFILE}

Sử dụng cắt ngắn:

BADLINESCOUNT=1
ORIGINALFILE=/tmp/whatever
truncate -s $(printf "$(stat --format=%s ${ORIGINALFILE}) - $(tail -n${BADLINESCOUNT} ${ORIGINALFILE} | wc -c)\n" | bc ) ${ORIGINALFILE}

Ôi. Cách quá phức tạp.
Robert

0

Linux

$ là dòng cuối cùng, d để xóa:

sed '$d' ~/path/to/your/file/name

Hệ điều hành Mac

Tương đương của sed -i

sed -i '' -e '$ d' ~/path/to/your/file/name

0

Đây là cách bạn có thể thực hiện thủ công (Cá nhân tôi sử dụng phương pháp này rất nhiều khi tôi cần nhanh chóng xóa dòng cuối cùng trong một tệp):

vim + [FILE]

Cái đó + dấu hiệu có nghĩa là khi các tập tin được mở ra trong trình soạn thảo văn bản vim, con trỏ sẽ được bố trí trên dòng cuối cùng của tập tin.

Bây giờ chỉ cần nhấn dhai lần trên bàn phím của bạn. Điều này sẽ làm chính xác những gì bạn muốn loại bỏ dòng cuối cùng. Sau đó, nhấn :theo sau xvà sau đó nhấn Enter. Điều này sẽ lưu các thay đổi và đưa bạn trở lại vỏ. Bây giờ, dòng cuối cùng đã được gỡ bỏ thành công.


2
Nhấn mạnh vào sự đơn giản đã bị mất ở đây
Peter M

-4

Ruby (1.9+)

ruby -ne 'BEGIN{prv=""};print prv ; prv=$_;' file
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.