ฉันจะลบบรรทัดแรกของไฟล์ข้อความโดยใช้สคริปต์ bash / sed ได้อย่างไร


554

ฉันจำเป็นต้องลบบรรทัดแรกออกจากไฟล์ข้อความขนาดใหญ่โดยใช้สคริปต์ทุบตี

ตอนนี้ฉันกำลังใช้sed -i -e "1d" $FILE- แต่ใช้เวลาประมาณหนึ่งนาทีในการลบ

มีวิธีที่มีประสิทธิภาพมากกว่านี้หรือไม่


-i หมายถึงอะไร
cikatomo

4
@cikatomo: มันย่อมาจากการแก้ไขแบบอินไลน์ - มันแก้ไขไฟล์ด้วยสิ่งที่คุณสร้าง
drewrockshard

4
หางเป็นช้ากว่า sed หางต้องการ 13.5s, sed ต้องการ 0.85s ไฟล์ของฉันมีเส้น ~ 1M ~ 100MB MacBook Air 2013 พร้อม SSD
jcsahnwaldt พูดว่า GoFundMonica

คำตอบ:


1029

ลองหาง :

tail -n +2 "$FILE"

-n x: เพียงพิมพ์xบรรทัดสุดท้าย tail -n 5จะให้คุณป้อน 5 บรรทัดสุดท้าย +ชนิดสัญญาณของการตีความอาร์กิวเมนต์และให้tailพิมพ์อะไร แต่แรกx-1สาย tail -n +1จะพิมพ์ไฟล์ทั้งหมดtail -n +2ทุกอย่างยกเว้นบรรทัดแรก ฯลฯ

GNU tailเร็วกว่าsedมาก tailยังมีอยู่ใน BSD และการ-n +2ตั้งค่าสถานะมีความสอดคล้องกันระหว่างเครื่องมือทั้งสอง ตรวจสอบหน้า man FreeBSDหรือOS Xเพิ่มเติม

รุ่น BSD อาจช้ากว่าsedมาก ฉันสงสัยว่าพวกเขาจัดการสิ่งนั้นอย่างไร tailควรอ่านไฟล์ทีละบรรทัดในขณะsedที่การดำเนินการที่ค่อนข้างซับซ้อนที่เกี่ยวข้องกับการตีความสคริปต์ใช้การแสดงออกปกติและไม่ชอบ

หมายเหตุ: คุณอาจถูกล่อลวงให้ใช้งาน

# THIS WILL GIVE YOU AN EMPTY FILE!
tail -n +2 "$FILE" > "$FILE"

แต่นี้จะทำให้คุณมีไฟล์ที่ว่างเปล่า เหตุผลก็คือการเปลี่ยนเส้นทาง ( >) เกิดขึ้นก่อนtailที่เชลล์จะเรียกใช้:

  1. เชลล์ตัดทอนไฟล์ $FILE
  2. เชลล์สร้างกระบวนการใหม่สำหรับ tail
  3. เชลล์เปลี่ยนเส้นทาง stdout ของtailกระบวนการเป็น$FILE
  4. tail อ่านจากตอนนี้ที่ว่างเปล่า $FILE

หากคุณต้องการลบบรรทัดแรกในไฟล์คุณควรใช้:

tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"

&&จะให้แน่ใจว่าไฟล์ที่ไม่ได้รับการเขียนทับเมื่อมีปัญหา


3
ตามss64.com/bash/tail.htmlนี้ค่าเริ่มต้นของบัฟเฟอร์ทั่วไปคือ 32k เมื่อใช้ BSD 'tail' พร้อม-rตัวเลือก อาจมีการตั้งค่าบัฟเฟอร์ในระบบหรือไม่? หรือ-nหมายเลขที่เซ็นชื่อ 32 บิตคืออะไร?
Yzmir Ramirez

41
@Eddie: user869097 บอกว่ามันไม่ทำงานเมื่อบรรทัดเดียวคือ 15Mb หรือมากกว่า ตราบใดที่เส้นสั้นลงtailก็จะใช้ได้กับไฟล์ทุกขนาด
Aaron Digulla

6
คุณสามารถอธิบายข้อโต้แย้งเหล่านี้ได้หรือไม่?
Dreampuf

17
@Dreampuf - จากหน้าคน:-n N means output the last N lines, instead of the last 10; or use +N to output lines starting with the Nth
Will Sheppard

11
ฉันจะเห็นด้วยกับ @JonaChristopherSahnwald - หางนั้นช้ากว่ารุ่น sed มากโดยเรียงตามลำดับความสำคัญ ฉันกำลังทดสอบไฟล์ 500,000KK เส้น (ไม่เกิน 50 ตัวอักษรต่อบรรทัด) อย่างไรก็ตามฉันรู้ว่าฉันใช้หางรุ่น FreeBSD (ซึ่งมาพร้อมกับ OS X เป็นค่าเริ่มต้น) เมื่อฉันเปลี่ยนเป็น GNU tail การเรียก tail นั้นเร็วกว่าการโทร Sed 10 ครั้ง (และ GNU Sed Call ด้วย) AaronDigulla ถูกต้องตรงนี้ถ้าคุณใช้ GNU
Dan Nguyen

179

คุณสามารถใช้ -i เพื่ออัปเดตไฟล์โดยไม่ต้องใช้ตัวดำเนินการ '>' คำสั่งต่อไปนี้จะลบบรรทัดแรกออกจากไฟล์และบันทึกลงในไฟล์

sed -i '1d' filename

1
ฉันได้รับข้อผิดพลาด:unterminated transform source string
Daniel Kobe

10
ใช้ได้ทุกครั้งและควรเป็นคำตอบที่ดีที่สุด!
xtheking

4
เพียงจำไว้ว่า Mac ต้องการส่วนต่อท้ายที่จะให้เมื่อใช้ sed กับการแก้ไขในสถานที่ ดังนั้นให้เรียกใช้ข้างต้นด้วย -i.bak
mjp

3
เพียงแค่ทราบ - เพื่อลบหลายบรรทัดใช้sed -i '1,2d' filename
เจ้าพ่อ

4
tail -n +2รุ่นนี้เป็นจริงมากขึ้นอ่านและสากลมากขึ้นกว่า ไม่แน่ใจว่าทำไมมันถึงไม่ใช่คำตอบอันดับต้น ๆ
ลุคเดวิส


17

ไม่มันมีประสิทธิภาพมากพอ ๆ กับที่คุณจะได้รับ คุณสามารถเขียนโปรแกรม C ซึ่งสามารถทำงานได้เร็วขึ้นเล็กน้อย (เวลาเริ่มต้นน้อยลงและการประมวลผลข้อโต้แย้ง) แต่มันอาจจะมีแนวโน้มที่จะมีความเร็วเท่ากับ sed เมื่อไฟล์มีขนาดใหญ่ (และฉันคิดว่ามันใหญ่ถ้าใช้เวลาสักครู่ )

แต่คำถามของคุณมีปัญหาจากปัญหาเดียวกันกับคำถามอื่น ๆ อีกมากมายที่จะแก้ปัญหาล่วงหน้า หากคุณกำลังจะบอกให้เราทราบในรายละเอียดสิ่งที่คุณกำลังพยายามที่จะทำแทนแล้วว่าเราอาจจะไม่สามารถที่จะขอแนะนำตัวเลือกที่ดี

ตัวอย่างเช่นหากนี่เป็นไฟล์ A ที่กระบวนการของโปรแกรม B อื่น ๆ โซลูชันหนึ่งจะไม่ดึงออกจากบรรทัดแรก แต่แก้ไขโปรแกรม B เพื่อประมวลผลแตกต่างกัน

สมมติว่าโปรแกรมทั้งหมดของคุณผนวกเข้ากับไฟล์ A และโปรแกรม B ในขณะนี้อ่านและประมวลผลบรรทัดแรกก่อนที่จะลบ

คุณสามารถสร้างโปรแกรม B อีกครั้งเพื่อที่จะไม่พยายามลบบรรทัดแรก แต่เก็บออฟเซ็ตถาวร (อาจเป็นไฟล์) ลงในไฟล์ A ดังนั้นครั้งต่อไปที่รันมันจะสามารถหาออฟเซ็ตได้ บรรทัดนั้นและอัพเดตออฟเซ็ต

จากนั้นในเวลาเงียบ (เที่ยงคืน?) มันสามารถทำการประมวลผลพิเศษของไฟล์ A เพื่อลบบรรทัดทั้งหมดที่ประมวลผลอยู่ในปัจจุบันและตั้งค่าออฟเซ็ตกลับเป็น 0

แน่นอนว่ามันจะเร็วกว่าสำหรับโปรแกรมที่จะเปิดและค้นหาไฟล์แทนที่จะเปิดและเขียนใหม่ การสนทนานี้จะถือว่าคุณสามารถควบคุมโปรแกรม B ได้แน่นอน ฉันไม่รู้ว่าเป็นกรณีนี้ แต่อาจมีวิธีแก้ไขปัญหาอื่น ๆ ที่เป็นไปได้หากคุณให้ข้อมูลเพิ่มเติม


ฉันคิดว่า OP พยายามบรรลุสิ่งที่ทำให้ฉันพบคำถามนี้ ฉันมีไฟล์ CSV 10 ไฟล์แต่ละบรรทัดมี 500k บรรทัด ทุกไฟล์มีแถวส่วนหัวเหมือนกับบรรทัดแรก I am cat: ing ไฟล์เหล่านี้เป็นไฟล์เดียวแล้วนำเข้าสู่ฐานข้อมูลเพื่อให้ DB สร้างชื่อคอลัมน์จากบรรทัดแรก เห็นได้ชัดว่าฉันไม่ต้องการให้บรรทัดนั้นซ้ำในไฟล์ 2-10
db

1
@ db ในกรณีawk FNR-1 *.csvนั้นอาจจะเร็วกว่า
jinawee

10

คุณสามารถแก้ไขไฟล์ได้: เพียงใช้-iธงของ perl ดังนี้:

perl -ni -e 'print unless $. == 1' filename.txt

สิ่งนี้ทำให้บรรทัดแรกหายไปตามที่คุณถาม Perl จะต้องอ่านและคัดลอกไฟล์ทั้งหมด แต่จะจัดให้มีการบันทึกผลลัพธ์ภายใต้ชื่อของไฟล์ต้นฉบับ


10

คุณสามารถทำสิ่งนี้กับ:

cat filename | sed 1d > filename_without_first_line

บนบรรทัดคำสั่ง หรือหากต้องการลบบรรทัดแรกของไฟล์อย่างถาวรให้ใช้โหมดแบบแทนที่พร้อมกับการ-iตั้งค่าสถานะ:

sed -i 1d <filename>

9

ดังที่ Pax กล่าวคุณอาจไม่ได้เร็วไปกว่านี้อีกแล้ว เหตุผลก็คือเกือบจะไม่มีระบบไฟล์ที่รองรับการตัดทอนจากจุดเริ่มต้นของไฟล์ดังนั้นนี่จะเป็นการดำเนินการ O ( n) ที่nมีขนาดของไฟล์ คุณสามารถทำอะไรได้มากขึ้นเร็วขึ้นก็คือการเขียนทับบรรทัดแรกมีหมายเลขเดียวกันของไบต์ (อาจจะมีช่องว่างหรือความคิดเห็น) ซึ่งอาจทำงานสำหรับคุณขึ้นอยู่กับสิ่งที่คุณกำลังพยายามที่จะทำ (อะไรที่อยู่โดยวิธีการ?)


เรื่อง"... แทบไม่มีระบบไฟล์ที่รองรับการตัดทอน ... " : มันน่าสนใจ โปรดพิจารณารวมถึงหมายเหตุที่สอดแทรกการตั้งชื่อระบบไฟล์
agc

1
@agc: ไม่เกี่ยวข้องตอนนี้ แต่งานแรกของฉันในยุค 70 คือกับ Quadex การเริ่มต้นเล็ก ๆ (ตอนนี้หายไปและไม่เกี่ยวข้องกับ บริษัท ทั้งสองตอนนี้ใช้ชื่อนั้น) พวกเขามีระบบไฟล์ที่อนุญาตให้เพิ่มหรือลบที่จุดเริ่มต้นหรือจุดสิ้นสุดของไฟล์ส่วนใหญ่จะใช้การแก้ไขในน้อยกว่า 3KB โดยวางเหนือหน้าต่างและด้านล่างหน้าต่างในไฟล์ มันไม่มีชื่อเป็นของตัวเองมันเป็นเพียงส่วนหนึ่งของ QMOS ระบบปฏิบัติการ Quadex Multiuser ('หลากหลาย' มักจะเป็น 2-3 บน LSI-11/02 ที่มี RAM ต่ำกว่า 64KB และโดยปกติแล้วฟล็อปปี้ดิสก์รุ่น RX01-type 8 "สองตัวในแต่ละ 250KB) :-)
dave_thompson_085

9

spongeutilหลีกเลี่ยงความจำเป็นสำหรับการเล่นกลไฟล์ temp นี้:

tail -n +2 "$FILE" | sponge "$FILE"

spongeแน่นอนว่าสะอาดกว่าและแข็งแกร่งกว่าโซลูชันที่ได้รับการยอมรับ ( tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE")
Jealie

1
ควรทำให้ชัดเจนว่า 'ฟองน้ำ' ต้องการแพคเกจ 'moreutils' ที่จะติดตั้ง
FedFranzoni

นี่เป็นทางออกเดียวที่ทำงานให้ฉันเปลี่ยนไฟล์ระบบ (บนอิมเมจ Debian) การแก้ปัญหาอื่น ๆ ล้มเหลวเนื่องจากข้อผิดพลาด "อุปกรณ์หรือทรัพยากรไม่ว่าง" เมื่อพยายามเขียนไฟล์
FedFranzoni

แต่spongeบัฟเฟอร์ไฟล์ทั้งหมดในหน่วยความจำ? ไม่สามารถใช้งานได้หากมีหลายร้อย GB
OrangeDog

@OrangeDog ตราบใดที่ระบบไฟล์สามารถเก็บไว้ได้ก็spongeจะดูดซับเนื่องจากมันใช้ไฟล์/ tmpเป็นขั้นตอนกลางซึ่งจะถูกใช้เพื่อแทนที่ต้นฉบับในภายหลัง
agc

8

หากคุณต้องการที่จะแก้ไขไฟล์ในสถานที่คุณสามารถใช้ต้นฉบับedแทนการทำต่อเนื่องของมันsed:

ed "$FILE" <<<$'1d\nwq\n'

edคำสั่งเป็นต้นฉบับแก้ไขข้อความ UNIX, ก่อนที่จะมีแม้กระทั่งขั้วแบบเต็มหน้าจอ, เวิร์คสเตชั่กราฟิกน้อยมาก exบรรณาธิการที่ดีที่สุดที่รู้จักกันเป็นสิ่งที่คุณกำลังใช้อยู่เมื่อพิมพ์ที่พร้อมรับคำลำไส้ใหญ่viเป็นอดีตรุ่นแนวโน้มของedดังนั้นหลายของการทำงานคำสั่งเดียวกัน ในขณะที่edตั้งใจที่จะใช้แบบโต้ตอบมันยังสามารถใช้ในโหมดแบทช์โดยการส่งคำสั่งสตริงซึ่งเป็นสิ่งที่วิธีการแก้ปัญหานี้

ลำดับ<<<$'1d\nwq\n'ใช้ประโยชน์จากการสนับสนุนของ Bash สำหรับ here-strings ( <<<) และอัญประกาศ POSIX ( $'... ') เพื่อป้อนข้อมูลedคำสั่งที่ประกอบด้วยสองบรรทัด: 1dซึ่งd จะลบบรรทัดที่1และจากนั้นwqซึ่งwจะไฟล์กลับไปที่ disk จากนั้นq uits เซสชันการแก้ไข


นี่คือสง่างาม +1
Armin

แต่คุณต้องอ่านไฟล์ทั้งหมดลงในหน่วยความจำซึ่งจะไม่ทำงานหากมีหลายร้อย GB
OrangeDog

5

ควรแสดงบรรทัดยกเว้นบรรทัดแรก:

cat textfile.txt | tail -n +2

4
- คุณ shoud ทำ "tail -n +2 textfile.txt"
niglesias

5
@ niglesiais ฉันไม่เห็นด้วยกับ "การใช้แมวที่ไร้ประโยชน์" เนื่องจากมันชัดเจนว่าโซลูชันนี้ใช้ได้กับเนื้อหาที่มีการเผยแพร่และไม่เพียง แต่ไฟล์เท่านั้น
Titou

5

สามารถใช้กลุ่มเพื่อทำสิ่งนี้:

vim -u NONE +'1d' +'wq!' /tmp/test.txt

สิ่งนี้ควรจะเร็วกว่าเนื่องจากกลุ่มจะไม่อ่านไฟล์ทั้งหมดเมื่อกระบวนการ


อาจต้องอ้าง+wq!ว่าถ้าเปลือกของคุณทุบตี อาจไม่ใช่เพราะคำว่า!ไม่ใช่จุดเริ่มต้นของคำ แต่การมีนิสัยในการอ้างถึงสิ่งต่าง ๆ น่าจะเป็นสิ่งที่ดีรอบตัว (และหากคุณต้องการประสิทธิภาพที่เหนือกว่าโดยไม่ต้องพูดถึงโดยไม่จำเป็นคุณไม่จำเป็นต้องใส่เครื่องหมายอัญประกาศ1d)
Mark Reed

เป็นกลุ่มไม่จำเป็นต้องอ่านไฟล์ทั้งหมด ในความเป็นจริงถ้าไฟล์มีขนาดใหญ่กว่าหน่วยความจำตามที่ถามใน Q นี้ vim อ่านไฟล์ทั้งหมดและเขียนมัน (หรือส่วนใหญ่ของมัน) ไปยังไฟล์ temp และหลังจากการแก้ไขเขียนมันทั้งหมดกลับไปที่ไฟล์ถาวร ฉันไม่รู้ว่าคุณคิดว่ามันจะทำงานได้อย่างไรหากปราศจากสิ่งนี้
dave_thompson_085

4

วิธีการเกี่ยวกับการใช้ csplit

man csplit
csplit -k file 1 '{1}'

รูปแบบนี้จะยังทำงาน แต่เพียง csplit file /^.*$/1แต่สร้างสองไฟล์ที่ส่งออกแทนสาม: csplit file //1หรือมากกว่าเพียง: หรือมากกว่านั้นเพียงแค่: csplit file 2.
Marco Roy

1

เนื่องจากดูเหมือนว่าฉันจะไม่สามารถเร่งความเร็วในการลบได้ฉันจึงคิดว่าวิธีที่ดีอาจจะดำเนินการกับไฟล์เป็นชุดดังนี้:

While file1 not empty
  file2 = head -n1000 file1
  process file2
  sed -i -e "1000d" file1
end

ข้อเสียของสิ่งนี้คือถ้าโปรแกรมถูกฆ่ากลาง (หรือถ้ามีบาง sql ไม่ดีในนั้น - ทำให้ส่วน "กระบวนการ" ตายหรือล็อค - ขึ้น) จะมีบรรทัดที่ถูกข้ามหรือถูกประมวลผลสองครั้ง .

(file1 มีบรรทัดของรหัส sql)


บรรทัดแรกมีอะไรบ้าง คุณสามารถเขียนทับความคิดเห็น sql ตามที่ฉันแนะนำในโพสต์ของฉันได้ไหม
Robert Gamble

0

หากสิ่งที่คุณต้องการทำคือกู้คืนหลังจากเกิดข้อผิดพลาดคุณสามารถสร้างไฟล์ที่มีสิ่งที่คุณทำจนถึงตอนนี้

if [[ -f $tmpf ]] ; then
    rm -f $tmpf
fi
cat $srcf |
    while read line ; do
        # process line
        echo "$line" >> $tmpf
    done

0

ซับนี้จะทำ:

echo "$(tail -n +2 "$FILE")" > "$FILE"

ทำงานได้เนื่องจากtailถูกดำเนินการมาก่อนechoแล้วจึงปลดล็อคไฟล์จึงไม่จำเป็นต้องใช้ไฟล์ชั่วคราว


-1

จะใช้หางบนเส้น N-1 และนำมันไปไว้ในไฟล์ตามด้วยการลบไฟล์เก่าและเปลี่ยนชื่อไฟล์ใหม่เป็นชื่อเก่าทำงานหรือไม่

ถ้าฉันทำสิ่งนี้โดยทางโปรแกรมฉันจะอ่านไฟล์และจำไฟล์ออฟเซ็ตหลังจากอ่านแต่ละบรรทัดดังนั้นฉันสามารถค้นหากลับไปที่ตำแหน่งนั้นเพื่ออ่านไฟล์ที่มีหนึ่งบรรทัดน้อยลง


ทางออกแรกนั้นเหมือนกับเบรนท์ที่กำลังทำอยู่ตอนนี้ ฉันไม่เข้าใจวิธีการเขียนโปรแกรมของคุณเฉพาะบรรทัดแรกเท่านั้นที่จะต้องลบคุณเพียงแค่อ่านและทิ้งบรรทัดแรกและคัดลอกที่เหลือไปยังไฟล์อื่นซึ่งเป็นอีกครั้งเหมือนกับวิธี sed และ tail
Robert Gamble

โซลูชันที่สองมีความหมายว่าไฟล์ไม่ได้หดตามบรรทัดแรกในแต่ละครั้ง โปรแกรมประมวลผลอย่างง่าย ๆ ราวกับว่ามันถูกหด แต่เริ่มต้นที่บรรทัดถัดไปในแต่ละครั้ง
EvilTeach

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