ลบบรรทัดสุดท้ายออกจากไฟล์ใน Bash


331

ฉันมีไฟล์foo.txt,, มีบรรทัดต่อไปนี้:

a
b
c

ฉันต้องการคำสั่งง่ายๆที่ส่งผลให้เนื้อหาของfoo.txtการเป็น:

a
b

คำตอบ:


407

การใช้GNU sed:

sed -i '$ d' foo.txt

-iตัวเลือกที่ไม่อยู่ในGNU sedรุ่นเก่ากว่า 3.95 เพื่อให้คุณมีที่จะใช้เป็นตัวกรองด้วยไฟล์ชั่วคราว:

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

แน่นอนในกรณีที่คุณยังสามารถใช้แทนhead -n -1sed

MacOS:

บน Mac OS X (จนถึง 10.7.4) เทียบเท่ากับsed -iคำสั่งด้านบนคือ

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

56
บน Mac OS X ( ณ วันที่ 10.7.4) เทียบเท่าของคำสั่งดังกล่าวมีsed -i sed -i '' -e '$ d' foo.txtนอกจากนี้head -n -1จะไม่ทำงานบน Mac ในปัจจุบัน
mklement0

26
คุณช่วยอธิบายว่า '$ d' regex ทำอะไรได้บ้าง เป็นคำถามเกี่ยวกับการนำบรรทัดสุดท้ายออกดังนั้นฉันคิดว่านี่เป็นส่วนที่สำคัญที่สุดสำหรับทุกคนที่ดูคำถามนี้ ขอบคุณ :)
kravemir

38
@Miro: ไม่ได้$ dเป็น regex มันเป็นคำสั่ง sed dคือคำสั่งสำหรับการลบบรรทัดในขณะที่$หมายถึง "บรรทัดสุดท้ายในไฟล์" เมื่อระบุตำแหน่ง (เรียกว่า "ช่วง" ใน sed lingo) หน้าคำสั่งคำสั่งนั้นจะถูกนำไปใช้กับตำแหน่งที่ระบุเท่านั้น ดังนั้นคำสั่งนี้จะพูดว่า "ในช่วงของบรรทัดสุดท้ายในไฟล์ลบออก" ค่อนข้างเนียนและตรงประเด็นถ้าคุณถามฉัน
Daniel Kamil Kozar

1
คุณจะลบบรรทัดสุดท้ายได้อย่างไร แต่ถ้าเป็นบรรทัดว่าง
skan

1
@Alex: อัพเดทสำหรับ GNU sed; ไม่ทราบเกี่ยวกับรุ่นต่างๆของ BSD / UNIX / MacOS ...
thkala

279

นี่เป็นวิธีที่เร็วและง่ายที่สุดโดยเฉพาะอย่างยิ่งในไฟล์ขนาดใหญ่:

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

ถ้าคุณต้องการที่จะลบบรรทัดด้านบนใช้สิ่งนี้:

tail -n +2 foo.txt

ซึ่งหมายความว่าบรรทัดเอาต์พุตเริ่มต้นที่บรรทัด 2

อย่าใช้sedเพื่อลบบรรทัดจากด้านบนหรือด้านล่างของไฟล์ - มันช้ามากถ้าไฟล์มีขนาดใหญ่


16
head -n -1 foo.txtเพียงพอแล้ว
ДМИТРИЙМАЛИКОВ

20
หัว -n -1 ไม่ทำงานบนหัวของ bdsutils อย่างน้อยไม่ได้ใช้ macos เวอร์ชันดังนั้นจึงใช้งานไม่ได้
johannes_lalala

23
@johannes_lalala บน Mac คุณสามารถbrew install coreutils(GNU สาธารณูปโภคหลัก) และใช้แทนghead head
น่าสนใจมี

นี่คือสคริปต์ทุบตีง่ายๆที่ทำให้มันเป็นอัตโนมัติในกรณีที่คุณมีหลายไฟล์ที่มีจำนวนบรรทัดต่าง ๆ ที่ด้านล่างเพื่อลบ: cat TAILfixer FILE=$1; TAIL=$2; head -n -${TAIL} ${FILE} > temp ; mv temp ${FILE}เรียกใช้สำหรับการลบ 4 บรรทัดสำหรับตัวอย่างจาก myfile เป็น: ./TAILfixer myfile 4แน่นอนทำให้มันปฏิบัติการโดยchmod +x TAILfixer
FatihSarigol

ยกนิ้วให้เพราะมันใช้งานได้ แต่สำหรับการเปรียบเทียบทั้งหมดsedคำสั่งนี้ค่อนข้างช้าเช่นกัน
Jeff Diederiks

121

ฉันมีปัญหากับคำตอบทั้งหมดที่นี่เพราะฉันทำงานกับไฟล์ขนาดใหญ่ (~ 300Gb) และไม่มีวิธีแก้ปัญหาใด ๆ นี่คือทางออกของฉัน:

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

ในคำ: ค้นหาความยาวของไฟล์ที่คุณต้องการท้ายด้วย (ความยาวของไฟล์ลบความยาวของบรรทัดสุดท้ายโดยใช้bc) และกำหนดตำแหน่งนั้นให้เป็นจุดสิ้นสุดของไฟล์ (โดยใช้ddหนึ่งไบต์ของ /dev/nullลงบน มัน).

นี้เป็นไปอย่างรวดเร็วเพราะtailการเริ่มต้นการอ่านจากสิ้นและddจะเขียนทับไฟล์ในสถานที่มากกว่าสำเนา (และแยก) บรรทัดของไฟล์ทุกซึ่งเป็นสิ่งที่โซลูชั่นอื่น ๆ ทำ

หมายเหตุ: นี่จะเป็นการลบบรรทัดออกจากไฟล์ในตำแหน่ง ทำสำเนาสำรองหรือทดสอบไฟล์ดัมมี่ก่อนลองใช้ไฟล์ของคุณเอง!


3
ฉันไม่รู้ด้วยซ้ำเกี่ยวกับวว นี่ควรเป็นคำตอบที่ดีที่สุด ขอบคุณมาก! มันเป็นความอัปยศวิธีแก้ปัญหาไม่สามารถใช้งานได้กับ (บล็อก) ไฟล์ gzipped
tommy.carstensen

4
แนวทางที่ฉลาดมาก! เพียงแค่ทราบ: มันต้องการและดำเนินการกับไฟล์จริงดังนั้นจึงไม่สามารถใช้กับไพพ์การทดแทนคำสั่งการเปลี่ยนทิศทางและการเชื่อมโยงดังกล่าว
MestreLion

3
นี่ควรเป็นคำตอบที่ดีที่สุด ทำงานได้ทันทีสำหรับไฟล์ ~ 8 GB
Peter Cogan

8
ผู้ใช้ mac: dd if = / dev / null ของ = <filename> bs = 1 ค้นหา = $ (echo $ (stat -f =% z <filename> | cut -c 2-) - $ (tail -n1 <filename> | wc -c) | bc)
Igor

2
วิธีนี้เป็นวิธีสำหรับไฟล์ขนาดใหญ่!
thomas.han

61

หากต้องการลบบรรทัดสุดท้ายออกจากไฟล์โดยไม่อ่านไฟล์ทั้งหมดหรือเขียนใหม่สิ่งใดคุณสามารถใช้

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

หากต้องการลบบรรทัดสุดท้ายและพิมพ์ลงบน stdout ("pop") คุณสามารถรวมคำสั่งนั้นเข้ากับtee:

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

คำสั่งเหล่านี้สามารถประมวลผลไฟล์ที่มีขนาดใหญ่มากได้อย่างมีประสิทธิภาพ คำตอบนี้คล้ายกับและได้รับแรงบันดาลใจจาก Yossi แต่ก็หลีกเลี่ยงการใช้ฟังก์ชั่นพิเศษบางอย่าง

หากคุณจะใช้สิ่งเหล่านี้ซ้ำ ๆ และต้องการจัดการข้อผิดพลาดและคุณสมบัติอื่น ๆ คุณสามารถใช้poptailคำสั่งได้ที่นี่: https://github.com/donm/evenmoreutils


3
บันทึกย่อ: truncateไม่สามารถใช้ได้ใน OS X โดยค่าเริ่มต้น แต่มันเป็นเรื่องง่ายที่จะติดตั้งโดยใช้ brew install truncateBrew:
Guillaume Boudreau

3
ดีมาก. ดีขึ้นมากโดยไม่ต้องวว ฉันกลัวที่จะใช้ dd เป็นหลักเดียวที่ใส่ผิดที่หรือตัวอักษรสามารถสะกดหายนะ .. +1
Yossi Farjoun

1
(คำเตือน JOKE) ที่จริงแล้ว ("dd" -> "ภัยพิบัติ") ต้องการการแทนที่ 1 รายการและการแทรก 6 ครั้ง "ตัวเลขหรือตัวอักษรหายไปเพียงอันเดียว" :)
Kyle

29

ผู้ใช้ Mac

หากคุณต้องการเพียงแค่เอาท์พุทบรรทัดสุดท้ายที่ถูกลบโดยไม่เปลี่ยนไฟล์เอง

sed -e '$ d' foo.txt

หากคุณต้องการลบบรรทัดสุดท้ายของอินพุตไฟล์เอง

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


สิ่งนี้ใช้ได้สำหรับฉัน แต่ฉันไม่เข้าใจ อย่างไรก็ตามมันใช้งานได้
Melvin

แฟ-i ''ล็กบอกให้ sed ปรับเปลี่ยนไฟล์แบบแทนที่ในขณะที่เก็บสำรองข้อมูลในไฟล์ที่มีนามสกุลที่ให้ไว้เป็นพารามิเตอร์ เนื่องจากพารามิเตอร์เป็นสตริงว่างไม่มีไฟล์สำรองถูกสร้างขึ้น -eบอกให้ sed ดำเนินการคำสั่ง คำสั่ง$ dหมายถึง: ค้นหาบรรทัดสุดท้าย ( $) และลบมัน ( d)
Krzysztof Szafranek

24

สำหรับผู้ใช้ Mac:

บน Mac หัว -n -1 ไม่ทำงาน และฉันพยายามหาวิธีแก้ปัญหาอย่างง่าย [โดยไม่ต้องกังวลเกี่ยวกับเวลาประมวลผล] เพื่อแก้ปัญหานี้โดยใช้คำสั่ง "head" และ / หรือ "tail" เท่านั้น

ฉันลองลำดับของคำสั่งต่อไปนี้และมีความสุขที่ฉันสามารถแก้มันได้โดยใช้คำสั่ง "tail" [พร้อมตัวเลือกที่มีใน Mac] ดังนั้นหากคุณใช้ Mac และต้องการใช้เฉพาะ "tail" เพื่อแก้ไขปัญหานี้คุณสามารถใช้คำสั่งนี้:

cat file.txt | tail -r | tail -n +2 | หาง -r

คำอธิบาย:

1> tail -r: กลับคำสั่งของเส้นในอินพุตของมัน

2> tail -n +2: นี่จะพิมพ์ทุกบรรทัดที่เริ่มต้นจากบรรทัดที่สองในการป้อนข้อมูล


2
การติดตั้ง coreutils โดยใช้ Homebrew จะทำให้คุณหัว GNU เป็น 'ghead' ช่วยให้คุณทำghead -n -1
ค่อนข้างออก

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

2
sed '$d'สำหรับการแก้ปัญหาตัวกรองที่ไม่ได้แก้ไขไฟล์ในสถานที่ใช้งาน
lhf

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

เป็นหลักรหัสนี้บอกว่าต่อไปนี้:

สำหรับแต่ละบรรทัดหลังจากบรรทัดแรกพิมพ์บรรทัดที่บัฟเฟอร์

สำหรับแต่ละบรรทัดรีเซ็ตบัฟเฟอร์

บัฟเฟอร์มีความยาวหนึ่งบรรทัดดังนั้นคุณจึงพิมพ์บรรทัดที่ 1 ถึง n-1


2
วิธีการแก้ปัญหานี้เป็นตัวกรองและไม่แก้ไขไฟล์ในสถานที่
lhf


0

โซลูชันทั้งสองนี้อยู่ในรูปแบบอื่น ฉันพบสิ่งเหล่านี้มีประโยชน์ชัดเจนและมีประโยชน์มากกว่านี้เล็กน้อย:

ใช้ 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}

ใช้ตัดทอน:

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

อุ๊ยตาย ทางซับซ้อนเกินไป
Robert

0

ลินุกซ์

$ คือบรรทัดสุดท้าย, d สำหรับลบ:

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

MacOS

เทียบเท่ากับ sed-i

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

0

นี่เป็นวิธีที่คุณสามารถทำได้ด้วยตนเอง (ฉันใช้วิธีนี้บ่อยๆเมื่อฉันต้องการลบบรรทัดสุดท้ายในไฟล์):

vim + [FILE]

ว่า+สัญญาณมีหมายความว่าเมื่อแฟ้มถูกเปิดในโปรแกรมแก้ไขข้อความที่เป็นกลุ่มเคอร์เซอร์จะถูกวางอยู่บนบรรทัดสุดท้ายของไฟล์

ตอนนี้เพียงแค่กด dสองครั้งบนแป้นพิมพ์ของคุณ สิ่งนี้จะทำสิ่งที่คุณต้องการอย่างแน่นอน - ลบบรรทัดสุดท้าย หลังจากนั้นให้กด:ตามมาด้วยและจากนั้นกดx Enterสิ่งนี้จะบันทึกการเปลี่ยนแปลงและนำคุณกลับไปที่เปลือก ตอนนี้ลบบรรทัดสุดท้ายเรียบร้อยแล้ว


2
การเน้นความเรียบง่ายหายไปที่นี่
Peter M

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.