วิธีตัดไฟล์ด้วยบรรทัด?


13

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

ตัวอย่างเช่นสมมติว่าฉันมีไฟล์ 120,000 ไบต์พร้อมเส้น 300- ไบต์และฉันพยายามตัดให้เหลือ 10,000 ไบต์ 33 บรรทัดแรกควรอยู่ (9900 ไบต์) และตัดส่วนที่เหลือออก ฉันไม่ต้องการตัดที่ 10,000 ไบต์อย่างแน่นอนเพราะจะทำให้มีบางส่วน

แน่นอนว่าไฟล์มีความยาวต่างกันและเส้นไม่ได้มีความยาวเท่ากันทั้งหมด

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


ลบคำตอบของฉัน ... ฉันเดาว่าขนาดไฟล์ในหน่วยไบต์ไม่ชัดเจนเกินไปขออภัย บางทีคุณสามารถแก้ไขคำถามของคุณและชี้แจงส่วนนั้น (เช่นตัวอย่าง)?
slhck

@slhck: ขอโทษที่เห็นคุณเสียตัวแทนเพียงเพราะฉันไม่ชัดเจน ... ให้ฉันดูว่าฉันสามารถแก้ไขได้
Charles

ไม่ต้องกังวลฉันควรจะได้ถามเพียงแค่ขอโทษ :)
slhck

คำตอบ:


1

sed/ wcซับซ้อนสามารถหลีกเลี่ยงได้ในคำตอบก่อนหน้านี้ถ้าawkถูกนำมาใช้ ใช้ตัวอย่างที่มีให้จาก OP (แสดงบรรทัดที่สมบูรณ์ก่อนหน้า 10,000 ไบต์):

awk '{i += (length() + 1); if (i <= 10000) print $ALL}' myfile.txt

ยังแสดงบรรทัดที่สมบูรณ์ที่มี 10,000 ไบต์ถ้าไบต์นั้นไม่อยู่ท้ายบรรทัด:

awk '{i += (length() + 1); print $ALL; if (i >= 10000) exit}' myfile.txt

คำตอบข้างต้นถือว่า:

  1. ไฟล์ข้อความของ Unix line terminator ( \n) สำหรับไฟล์ข้อความ Dos / Windows ( \r\n) เปลี่ยนlength() + 1เป็นlength() + 2
  2. ไฟล์ข้อความมีอักขระไบต์เดียวเท่านั้น หากมีอักขระหลายไบต์ (เช่นในสภาพแวดล้อม unicode) ให้ตั้งค่าสภาพแวดล้อมLC_CTYPE=Cเพื่อบังคับใช้การตีความในระดับไบต์

15

sedวิธีดี แต่ห่วงผ่านสายทั้งหมดไม่ได้ หากคุณทราบจำนวนบรรทัดที่คุณต้องการเก็บไว้ (เพื่อเป็นตัวอย่างฉันใช้ 99 ตรงนี้) คุณสามารถทำสิ่งนี้ได้:

sed -i '100,$ d' myfile.txt

คำอธิบาย: sedเป็นตัวประมวลผลนิพจน์ทั่วไป ด้วยตัวเลือกที่-iกำหนดมันประมวลผลไฟล์โดยตรง ("inline") - แทนที่จะอ่านมันและเขียนผลลัพธ์ไปยังเอาต์พุตมาตรฐาน 100,$เพียงแค่หมายถึง "จากบรรทัด 100 ถึงท้ายไฟล์" - และตามด้วยคำสั่งdซึ่งคุณอาจเดาได้อย่างถูกต้องว่าจะ "ลบ" ดังนั้นในระยะสั้นคำสั่งหมายถึง: "ลบทุกบรรทัดจากบรรทัด 100 ไปยังจุดสิ้นสุดของไฟล์จาก myfile.txt" 100 คือบรรทัดแรกที่จะลบตามที่คุณต้องการเก็บ 99 บรรทัด

แก้ไข:หากมีไฟล์บันทึกที่คุณต้องการเก็บไว้เช่น100 บรรทัดสุดท้าย :

[ $(wc -l myfile.txt) -gt 100 ] && sed -i "1,$(($(wc -l myfile.txt|awk '{print $1}') - 100)) d" myfile.txt

เกิดขึ้นที่นี่คืออะไร:

  • [ $(wc -l myfile.txt) -gt 100 ]: ทำสิ่งต่อไปนี้หากไฟล์มีมากกว่า 100 บรรทัด
  • $((100 - $(wc -l myfile.txt|awk '{print $1}'))): คำนวณจำนวนบรรทัดที่จะลบ (เช่นทุกบรรทัดของไฟล์ยกเว้น (สุดท้าย) 100 เพื่อเก็บไว้)
  • 1, $((..)) d: ลบทุกบรรทัดจากบรรทัดแรกถึงบรรทัดที่คำนวณ

แก้ไข:เนื่องจากคำถามเพิ่งแก้ไขเพื่อให้รายละเอียดเพิ่มเติมฉันจะรวมข้อมูลเพิ่มเติมนี้ไว้ในคำตอบของฉันด้วย ข้อเท็จจริงที่เพิ่มเข้ามาคือ:

  • ขนาดที่ระบุจะยังคงอยู่กับไฟล์ (10,000 ไบต์)
  • แต่ละบรรทัดมีขนาดเฉพาะเป็นไบต์ (ตัวอย่างเช่น 300 ไบต์)

จากข้อมูลเหล่านี้มีความเป็นไปได้ที่จะคำนวณจำนวนบรรทัดที่จะยังคงเป็น "/" ซึ่งมีตัวอย่างหมายถึง 33 บรรทัด คำว่าเชลล์สำหรับการคำนวณ: $((size_to_remain / linesize))(อย่างน้อยบน Linux ที่ใช้ Bash ผลลัพธ์จะเป็นจำนวนเต็ม) คำสั่งที่ปรับแล้วตอนนี้จะอ่าน:

# keep the start of the file (OPs question)
sed -i '34,$ d' myfile.txt
# keep the end of the file (my second example)
[ $(wc -l myfile.txt) -gt 33 ] && sed -i "1,33 d" myfile.txt

เนื่องจากขนาดเป็นที่รู้จักล่วงหน้าจึงไม่จำเป็นต้องมีการคำนวณที่ฝังอยู่ในsedคำสั่งอีกต่อไป แต่เพื่อความยืดหยุ่นภายในเชลล์สคริปต์บางตัวสามารถใช้ตัวแปรได้

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

[ "$(ls -lk $file | awk ' {print $5}')" -gt 100 ] &&

ซึ่งหมายความว่า: "ถ้าขนาด$fileเกินกว่า 100kB ทำ ... " ( ls -lkแสดงขนาดไฟล์เป็น kB ที่ตำแหน่ง 5 ดังนั้นawkจะใช้เพื่อแยกข้อมูลนี้ออกมาอย่างแน่นอน)


OP ต้องการตัดไฟล์ตามขนาดไบต์ที่แน่นอนไม่ใช่เพียงแค่ความยาวในแง่ของบรรทัด ฉันได้ลบคำตอบที่เกี่ยวข้องhead -nแล้ว
slhck

@slhck ขอบคุณสำหรับการแจ้งเตือน ใช่ OP เพิ่งแก้ไขคำถามของเขาเพื่อทำให้ความตั้งใจชัดเจนยิ่งขึ้น ในขณะที่เขามีวิธีการคำนวณจำนวนไบต์แต่ละบรรทัดคำตอบของฉันยังคงถูกต้องตามหลักการ - ในขณะที่เขาสามารถคำนวณจำนวนบรรทัดที่เหลือจากนั้นใช้วิธีการของฉันในการจัดการไฟล์ บางทีฉันอาจพูดสั้น ๆ เกี่ยวกับสิ่งนั้นในคำตอบของฉัน
Izzy

ไม่ - ไม่ทราบขนาดล่วงหน้า นั่นคือตัวอย่าง แต่ละไฟล์จะมีขนาดและบรรทัดแตกต่างกันซึ่งมีความยาวไม่สม่ำเสมอ ไฟล์บางไฟล์ไม่จำเป็นต้องถูกตัดทอนเลย
ชาร์ลส์

โอ้อีกครั้ง ... บางสิ่งยากที่จะอธิบายอย่างชัดเจน (มีหลาย facettes) สำหรับไฟล์ที่ไม่ต้องการตัดทอนอาจเป็นไปตามขนาดไฟล์? ที่สามารถครอบคลุมได้ แต่ถ้าไม่มีขนาดของเส้นเฉลี่ยที่ทราบส่วนนี้จะยาก - ฉันไม่สามารถคิดถึงวิธีแก้ปัญหาที่ง่าย (โดยไม่มีค่าใช้จ่ายมากเกินไป) ในขณะนี้
อิซซี่

ทั้งหมดที่ฉันสามารถเกิดขึ้นได้ในขณะนี้จะเกี่ยวข้องกับเช่นรับ n บรรทัดแรกคำนวณความยาวเฉลี่ยตามพวกเขาและใช้ค่านี้ สิ่งนั้นจะช่วยคุณได้ไหม
Izzy

0

ไม่พบคำสั่งในการทำสิ่งนี้ฉันเขียนสคริปต์สั้น ๆ (ไม่ทดสอบ):

#!/bin/sh

# Usage: $0 glob.* 25000
# where glob.* is a wildcard pattern and 25000 is the maximum number of bytes.

limit=20000
tmp=/tmp/trim
[[ "$2" == +([0-9]) ]] || limit=$2
limit=`expr $len + 1`
for file in $1;
do
    [[ `wc -c $file` -lt $limit ]] && continue
    head -c $file > $tmp
    sed '$d' $tmp
    $tmp > $file
done

-1

คุณสามารถใช้คำสั่ง linux sed เพื่อลบบรรทัดออกจากไฟล์ คำสั่งต่อไปนี้จะลบบรรทัดสุดท้ายของ filename.txt:

sed '$d' filename.txt

ด้วย awk หรือค้นหาคุณสามารถค้นหารูปแบบที่ตรงกับคำสั่ง sed ของคุณ ก่อนอื่นคุณค้นหาด้วย awk หรือค้นหาไฟล์ที่คุณต้องการตัดให้สั้นลงจากนั้นคุณสามารถลบบรรทัดด้วย sed


-1

ฉันทำอะไรที่คล้ายกับหาง วิธีเก็บเฉพาะ 10,000 บรรทัดสุดท้ายในกรณีนี้:

TMP=$(tail -n 10000 /path/to/some/file 2>/dev/null) && echo "${TMP}" > /path/to/some/file
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.