ฉันจำเป็นต้องลบบรรทัดแรกออกจากไฟล์ข้อความขนาดใหญ่โดยใช้สคริปต์ทุบตี
ตอนนี้ฉันกำลังใช้sed -i -e "1d" $FILE
- แต่ใช้เวลาประมาณหนึ่งนาทีในการลบ
มีวิธีที่มีประสิทธิภาพมากกว่านี้หรือไม่
ฉันจำเป็นต้องลบบรรทัดแรกออกจากไฟล์ข้อความขนาดใหญ่โดยใช้สคริปต์ทุบตี
ตอนนี้ฉันกำลังใช้sed -i -e "1d" $FILE
- แต่ใช้เวลาประมาณหนึ่งนาทีในการลบ
มีวิธีที่มีประสิทธิภาพมากกว่านี้หรือไม่
คำตอบ:
ลองหาง :
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
ที่เชลล์จะเรียกใช้:
$FILE
tail
tail
กระบวนการเป็น$FILE
tail
อ่านจากตอนนี้ที่ว่างเปล่า $FILE
หากคุณต้องการลบบรรทัดแรกในไฟล์คุณควรใช้:
tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"
&&
จะให้แน่ใจว่าไฟล์ที่ไม่ได้รับการเขียนทับเมื่อมีปัญหา
-r
ตัวเลือก อาจมีการตั้งค่าบัฟเฟอร์ในระบบหรือไม่? หรือ-n
หมายเลขที่เซ็นชื่อ 32 บิตคืออะไร?
tail
ก็จะใช้ได้กับไฟล์ทุกขนาด
-n N means output the last N lines, instead of the last 10; or use +N to output lines starting with the Nth
คุณสามารถใช้ -i เพื่ออัปเดตไฟล์โดยไม่ต้องใช้ตัวดำเนินการ '>' คำสั่งต่อไปนี้จะลบบรรทัดแรกออกจากไฟล์และบันทึกลงในไฟล์
sed -i '1d' filename
unterminated transform source string
sed -i '1,2d' filename
tail -n +2
รุ่นนี้เป็นจริงมากขึ้นอ่านและสากลมากขึ้นกว่า ไม่แน่ใจว่าทำไมมันถึงไม่ใช่คำตอบอันดับต้น ๆ
สำหรับผู้ที่อยู่บน SunOS ซึ่งไม่ใช่ GNU รหัสต่อไปนี้จะช่วย:
sed '1d' test.dat > tmp.dat
ไม่มันมีประสิทธิภาพมากพอ ๆ กับที่คุณจะได้รับ คุณสามารถเขียนโปรแกรม C ซึ่งสามารถทำงานได้เร็วขึ้นเล็กน้อย (เวลาเริ่มต้นน้อยลงและการประมวลผลข้อโต้แย้ง) แต่มันอาจจะมีแนวโน้มที่จะมีความเร็วเท่ากับ sed เมื่อไฟล์มีขนาดใหญ่ (และฉันคิดว่ามันใหญ่ถ้าใช้เวลาสักครู่ )
แต่คำถามของคุณมีปัญหาจากปัญหาเดียวกันกับคำถามอื่น ๆ อีกมากมายที่จะแก้ปัญหาล่วงหน้า หากคุณกำลังจะบอกให้เราทราบในรายละเอียดสิ่งที่คุณกำลังพยายามที่จะทำแทนแล้วว่าเราอาจจะไม่สามารถที่จะขอแนะนำตัวเลือกที่ดี
ตัวอย่างเช่นหากนี่เป็นไฟล์ A ที่กระบวนการของโปรแกรม B อื่น ๆ โซลูชันหนึ่งจะไม่ดึงออกจากบรรทัดแรก แต่แก้ไขโปรแกรม B เพื่อประมวลผลแตกต่างกัน
สมมติว่าโปรแกรมทั้งหมดของคุณผนวกเข้ากับไฟล์ A และโปรแกรม B ในขณะนี้อ่านและประมวลผลบรรทัดแรกก่อนที่จะลบ
คุณสามารถสร้างโปรแกรม B อีกครั้งเพื่อที่จะไม่พยายามลบบรรทัดแรก แต่เก็บออฟเซ็ตถาวร (อาจเป็นไฟล์) ลงในไฟล์ A ดังนั้นครั้งต่อไปที่รันมันจะสามารถหาออฟเซ็ตได้ บรรทัดนั้นและอัพเดตออฟเซ็ต
จากนั้นในเวลาเงียบ (เที่ยงคืน?) มันสามารถทำการประมวลผลพิเศษของไฟล์ A เพื่อลบบรรทัดทั้งหมดที่ประมวลผลอยู่ในปัจจุบันและตั้งค่าออฟเซ็ตกลับเป็น 0
แน่นอนว่ามันจะเร็วกว่าสำหรับโปรแกรมที่จะเปิดและค้นหาไฟล์แทนที่จะเปิดและเขียนใหม่ การสนทนานี้จะถือว่าคุณสามารถควบคุมโปรแกรม B ได้แน่นอน ฉันไม่รู้ว่าเป็นกรณีนี้ แต่อาจมีวิธีแก้ไขปัญหาอื่น ๆ ที่เป็นไปได้หากคุณให้ข้อมูลเพิ่มเติม
awk FNR-1 *.csv
นั้นอาจจะเร็วกว่า
คุณสามารถแก้ไขไฟล์ได้: เพียงใช้-i
ธงของ perl ดังนี้:
perl -ni -e 'print unless $. == 1' filename.txt
สิ่งนี้ทำให้บรรทัดแรกหายไปตามที่คุณถาม Perl จะต้องอ่านและคัดลอกไฟล์ทั้งหมด แต่จะจัดให้มีการบันทึกผลลัพธ์ภายใต้ชื่อของไฟล์ต้นฉบับ
คุณสามารถทำสิ่งนี้กับ:
cat filename | sed 1d > filename_without_first_line
บนบรรทัดคำสั่ง หรือหากต้องการลบบรรทัดแรกของไฟล์อย่างถาวรให้ใช้โหมดแบบแทนที่พร้อมกับการ-i
ตั้งค่าสถานะ:
sed -i 1d <filename>
ดังที่ Pax กล่าวคุณอาจไม่ได้เร็วไปกว่านี้อีกแล้ว เหตุผลก็คือเกือบจะไม่มีระบบไฟล์ที่รองรับการตัดทอนจากจุดเริ่มต้นของไฟล์ดังนั้นนี่จะเป็นการดำเนินการ O ( n
) ที่n
มีขนาดของไฟล์ คุณสามารถทำอะไรได้มากขึ้นเร็วขึ้นก็คือการเขียนทับบรรทัดแรกมีหมายเลขเดียวกันของไบต์ (อาจจะมีช่องว่างหรือความคิดเห็น) ซึ่งอาจทำงานสำหรับคุณขึ้นอยู่กับสิ่งที่คุณกำลังพยายามที่จะทำ (อะไรที่อยู่โดยวิธีการ?)
sponge
utilหลีกเลี่ยงความจำเป็นสำหรับการเล่นกลไฟล์ temp นี้:
tail -n +2 "$FILE" | sponge "$FILE"
sponge
แน่นอนว่าสะอาดกว่าและแข็งแกร่งกว่าโซลูชันที่ได้รับการยอมรับ ( tail -n +2 "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"
)
sponge
บัฟเฟอร์ไฟล์ทั้งหมดในหน่วยความจำ? ไม่สามารถใช้งานได้หากมีหลายร้อย GB
sponge
จะดูดซับเนื่องจากมันใช้ไฟล์/ tmpเป็นขั้นตอนกลางซึ่งจะถูกใช้เพื่อแทนที่ต้นฉบับในภายหลัง
หากคุณต้องการที่จะแก้ไขไฟล์ในสถานที่คุณสามารถใช้ต้นฉบับ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 เซสชันการแก้ไข
สามารถใช้กลุ่มเพื่อทำสิ่งนี้:
vim -u NONE +'1d' +'wq!' /tmp/test.txt
สิ่งนี้ควรจะเร็วกว่าเนื่องจากกลุ่มจะไม่อ่านไฟล์ทั้งหมดเมื่อกระบวนการ
+wq!
ว่าถ้าเปลือกของคุณทุบตี อาจไม่ใช่เพราะคำว่า!
ไม่ใช่จุดเริ่มต้นของคำ แต่การมีนิสัยในการอ้างถึงสิ่งต่าง ๆ น่าจะเป็นสิ่งที่ดีรอบตัว (และหากคุณต้องการประสิทธิภาพที่เหนือกว่าโดยไม่ต้องพูดถึงโดยไม่จำเป็นคุณไม่จำเป็นต้องใส่เครื่องหมายอัญประกาศ1d
)
วิธีการเกี่ยวกับการใช้ csplit
man csplit
csplit -k file 1 '{1}'
csplit file /^.*$/1
แต่สร้างสองไฟล์ที่ส่งออกแทนสาม: csplit file //1
หรือมากกว่าเพียง: หรือมากกว่านั้นเพียงแค่: csplit file 2
.
เนื่องจากดูเหมือนว่าฉันจะไม่สามารถเร่งความเร็วในการลบได้ฉันจึงคิดว่าวิธีที่ดีอาจจะดำเนินการกับไฟล์เป็นชุดดังนี้:
While file1 not empty
file2 = head -n1000 file1
process file2
sed -i -e "1000d" file1
end
ข้อเสียของสิ่งนี้คือถ้าโปรแกรมถูกฆ่ากลาง (หรือถ้ามีบาง sql ไม่ดีในนั้น - ทำให้ส่วน "กระบวนการ" ตายหรือล็อค - ขึ้น) จะมีบรรทัดที่ถูกข้ามหรือถูกประมวลผลสองครั้ง .
(file1 มีบรรทัดของรหัส sql)
หากสิ่งที่คุณต้องการทำคือกู้คืนหลังจากเกิดข้อผิดพลาดคุณสามารถสร้างไฟล์ที่มีสิ่งที่คุณทำจนถึงตอนนี้
if [[ -f $tmpf ]] ; then
rm -f $tmpf
fi
cat $srcf |
while read line ; do
# process line
echo "$line" >> $tmpf
done
ซับนี้จะทำ:
echo "$(tail -n +2 "$FILE")" > "$FILE"
ทำงานได้เนื่องจากtail
ถูกดำเนินการมาก่อนecho
แล้วจึงปลดล็อคไฟล์จึงไม่จำเป็นต้องใช้ไฟล์ชั่วคราว
จะใช้หางบนเส้น N-1 และนำมันไปไว้ในไฟล์ตามด้วยการลบไฟล์เก่าและเปลี่ยนชื่อไฟล์ใหม่เป็นชื่อเก่าทำงานหรือไม่
ถ้าฉันทำสิ่งนี้โดยทางโปรแกรมฉันจะอ่านไฟล์และจำไฟล์ออฟเซ็ตหลังจากอ่านแต่ละบรรทัดดังนั้นฉันสามารถค้นหากลับไปที่ตำแหน่งนั้นเพื่ออ่านไฟล์ที่มีหนึ่งบรรทัดน้อยลง