วิธีลบบรรทัดสุดท้ายของไฟล์ทั้งหมดออกจากสารบบ?


17

ฉันมีไฟล์ข้อความจำนวนมากในไดเรกทอรีและฉันต้องการลบบรรทัดสุดท้ายของทุกไฟล์ในไดเรกทอรี

ฉันจะทำมันได้อย่างไร


6
คุณลองทำอะไร unix.stackexchange.com/help/how-to-ask : "การแชร์การวิจัยของคุณช่วยให้ทุกคนบอกสิ่งที่คุณพบและสาเหตุที่มันไม่ตรงกับความต้องการของคุณสิ่งนี้แสดงให้เห็นว่าคุณใช้เวลาในการพยายามช่วยเหลือตัวเอง มันช่วยให้เราไม่ต้องย้ำคำตอบที่ชัดเจนและเหนือสิ่งอื่นมันช่วยให้คุณได้คำตอบที่เฉพาะเจาะจงมากขึ้น!
แพทริค

เหตุใดความคิดของคนที่ตั้งใจใช้สิ่งนั้นกับ / etc จึงมีคุณภาพที่พิเศษมากในการเหนี่ยวนำการประจบประแจง :)
rackandboneman

คำตอบ:


4

หากคุณมีสิทธิ์เข้าถึงvimคุณสามารถใช้:

for file in ./*
do
  if [ -f "${file}" ]
  then
    vim -c '$d' -c "wq" "${file}"
  fi
done

16

คุณสามารถใช้ oneliner ดีถ้าคุณมี sedGNU

 sed -i '$ d' ./*

มันจะลบบรรทัดสุดท้ายของไฟล์ที่ไม่ได้ซ่อนไว้ในไดเรกทอรีปัจจุบัน Switch -iสำหรับ GNU sedหมายถึงการทำงานในสถานที่และ'$ d'คำสั่งsedเพื่อลบบรรทัดสุดท้าย ( $หมายถึงล่าสุดและdความหมายลบ)


3
สิ่งนี้จะทำให้เกิดข้อผิดพลาด (และไม่ทำอะไรเลย) หากโฟลเดอร์มีสิ่งอื่นนอกเหนือจากไฟล์ปกติเช่นโฟลเดอร์อื่น ...
Najib Idrissi

1
@StefanR คุณกำลังใช้งาน-iซึ่งเป็น GNUism ดังนั้นนี่คือ moot แต่ฉันจะเสียหนวดเคราเก่าของฉันถ้าฉันไม่สามารถชี้ให้เห็นได้ว่ารุ่นเก่าบางรุ่นsedไม่อนุญาตให้คุณวางช่องว่างระหว่าง$และd(หรือ โดยทั่วไประหว่างรูปแบบและคำสั่ง)
zwol

1
@zwol ตามที่ฉันเขียนสิ่งนี้จะส่งผลให้เกิดข้อผิดพลาดไม่ใช่คำเตือนและ sed จะยอมแพ้เมื่อถึงไฟล์นั้น (อย่างน้อยกับรุ่น sed ฉันมี) ไฟล์ถัดไปจะไม่ถูกประมวลผล การทิ้งข้อความแสดงข้อผิดพลาดเป็นความคิดที่แย่มากเพราะคุณไม่รู้ว่ามันเกิดขึ้น! ด้วย zsh คุณสามารถใช้*(.)เพื่อ glob สำหรับไฟล์ปกติฉันไม่รู้เกี่ยวกับเชลล์อื่น ๆ
Najib Idrissi

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

@don_crissti ฉันมี GNU sed v4.3 ด้วย ... ฉันไม่รู้ว่าจะบอกอะไรกับคุณฉันเพิ่งทดสอบอีกครั้ง gist.github.com/nidrissi/66fad6be334234f5dbb41c539d84d61e
Najib Idrissi

11

คำตอบอื่น ๆ ทั้งหมดมีปัญหาหากไดเรกทอรีมีสิ่งอื่นที่ไม่ใช่ไฟล์ปกติหรือไฟล์ที่มีช่องว่าง / บรรทัดใหม่ในชื่อไฟล์ นี่คือสิ่งที่ทำงานโดยไม่คำนึงถึง:

find "$dir" -type f -exec sed -i '$d' '{}' '+'
  • find "$dir" -type f: ค้นหาไฟล์ในไดเรกทอรี $dir
    • -type f ซึ่งเป็นไฟล์ปกติ
    • -exec รันคำสั่งในแต่ละไฟล์ที่พบ
    • sed -i: แก้ไขไฟล์ในสถานที่;
    • '$d': delete d( $) บรรทัดสุดท้าย ( )
    • '+': บอกให้ค้นหาเพื่อเพิ่มอาร์กิวเมนต์ต่อไปsed(ให้มีประสิทธิภาพมากกว่าการรันคำสั่งสำหรับแต่ละไฟล์แยกกันขอบคุณ @zwol)

หากคุณไม่ต้องการที่จะลงไปในไดเรกทอรีย่อยแล้วคุณสามารถเพิ่มการโต้แย้งไป-maxdepth 1find


1
อืม แต่ไม่เหมือนคำตอบอื่น ๆ นี้ให้สิ้นซากลงในไดเรกทอรีย่อย (ยังมีรุ่นปัจจุบันของfindนี้ถูกเขียนได้อย่างมีประสิทธิภาพมากขึ้นfind $dir -type f -exec sed -i '$d' '{}' '+'.)
zwol

@zwol ขอบคุณฉันได้เพิ่มเข้าไปในคำตอบ
Najib Idrissi

-print0ไม่ได้มีอยู่ในคำสั่งเต็มทำไมใส่มันเข้าไปในคำอธิบาย?
Ruslan

1
นอกจากนี้-depth 0ไม่ทำงาน (findutils 4.4.2) -maxdepth 1มันควรจะเป็น
Ruslan

@Ruslan ผมมีรุ่นแรกที่ผมใช้ xargs -execแต่แล้วจำ
Najib Idrissi

9

การใช้ GNU sed -i '$d'หมายถึงการอ่านไฟล์เต็มและสร้างสำเนาโดยไม่มีบรรทัดสุดท้ายในขณะที่มันจะมีประสิทธิภาพมากกว่าที่จะตัดทอนไฟล์ที่อยู่ในตำแหน่ง (อย่างน้อยสำหรับไฟล์ขนาดใหญ่)

ด้วย GNU truncateคุณสามารถทำได้:

for file in ./*; do
  [ -f "$file" ] &&
    length=$(tail -n 1 "$file" | wc -c) &&
    [ "$length" -gt 0 ] &&
    truncate -s "-$length" "$file"
done

หากไฟล์มีขนาดค่อนข้างเล็กอาจจะมีประสิทธิภาพลดลงแม้ว่าจะรันหลายคำสั่งต่อไฟล์

โปรดทราบว่าสำหรับไฟล์ที่มีไบต์พิเศษหลังจากอักขระบรรทัดใหม่ล่าสุด (หลังบรรทัดสุดท้าย) หรือกล่าวอีกนัยหนึ่งหากมีบรรทัดสุดท้ายที่ไม่มีการคั่นแล้วขึ้นอยู่กับtailการนำไปใช้งานtail -n 1จะส่งกลับเฉพาะไบต์พิเศษเหล่านั้น (เช่น GNU tail) หรือบรรทัดสุดท้าย (คั่นอย่างถูกต้อง) และไบต์พิเศษเหล่านั้น


คุณต้องใช้|wc -cในtailการโทร? (หรือ a ${#length})
Jeff Schaller

@JeffSchaller อุ่ย ห้องสุขา -c ตั้งใจไว้อย่างแน่นอน ${#length}จะไม่ทำงานเนื่องจากมีการนับอักขระไม่ใช่ไบต์และ$(...)จะลบอักขระบรรทัดใหม่ที่มีหางซึ่ง${#...}จะปิดโดยหนึ่งแม้ว่าอักขระทั้งหมดจะเป็นไบต์เดียว
Stéphane Chazelas

6

วิธีการพกพามากขึ้น:

for f in ./*
do
test -f "$f" && ed -s "$f" <<\IN
d
w
q
IN
done

ฉันไม่คิดว่านี่จะต้องมีคำอธิบายใด ๆ ... ยกเว้นอาจเป็นไปได้ว่าในกรณีนี้dเป็นเช่นเดียวกัน$dเนื่องจากedโดยค่าเริ่มต้นเลือกบรรทัดสุดท้าย
สิ่งนี้จะไม่ค้นหาซ้ำและจะไม่ประมวลผลไฟล์ที่ซ่อนอยู่ (aka dotfiles)
หากคุณต้องการแก้ไขเหล่านั้นก็จะเห็นวิธีจับคู่ * กับไฟล์ที่ซ่อนอยู่ในไดเรกทอรีด้วย


ดี! หากคุณเปลี่ยน[[]]ไป[]จะเป็นไปตาม POSIX อย่างสมบูรณ์ ( [[ ... ]]คือ Bashism)
Wildcard

@ Wildcard - ขอบคุณเปลี่ยนไป (แม้ว่า[[จะไม่ใช่ bashism )
don_crissti

ไม่ใช่ POSIX-ism ฉันควรจะพูด :)
ไวด์การ์ด

3

POSIX ที่สอดคล้องกับหนึ่งซับสำหรับไฟล์ทั้งหมดที่เริ่มต้นซ้ำในไดเรกทอรีปัจจุบันรวมถึงจุดไฟล์:

find . -type f -exec sh -c 'for f; do printf "\$d\nx\n" | ex "$f"; done' sh {} +

สำหรับ.txtไฟล์เท่านั้นไม่ใช่แบบเรียกซ้ำ:

find . -path '*/*/*' -prune -o -type f -name '*.txt' -exec sh -c 'for f; do printf "\$d\nx\n" | ex "$f"; done' sh {} +

ดูเพิ่มเติมที่:

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