ฉันมีไฟล์ข้อความจำนวนมากในไดเรกทอรีและฉันต้องการลบบรรทัดสุดท้ายของทุกไฟล์ในไดเรกทอรี
ฉันจะทำมันได้อย่างไร
ฉันมีไฟล์ข้อความจำนวนมากในไดเรกทอรีและฉันต้องการลบบรรทัดสุดท้ายของทุกไฟล์ในไดเรกทอรี
ฉันจะทำมันได้อย่างไร
คำตอบ:
หากคุณมีสิทธิ์เข้าถึงvimคุณสามารถใช้:
for file in ./*
do
if [ -f "${file}" ]
then
vim -c '$d' -c "wq" "${file}"
fi
done
คุณสามารถใช้ oneliner ดีถ้าคุณมี sedGNU
sed -i '$ d' ./*
มันจะลบบรรทัดสุดท้ายของไฟล์ที่ไม่ได้ซ่อนไว้ในไดเรกทอรีปัจจุบัน Switch -iสำหรับ GNU sedหมายถึงการทำงานในสถานที่และ'$ d'คำสั่งsedเพื่อลบบรรทัดสุดท้าย ( $หมายถึงล่าสุดและdความหมายลบ)
-iซึ่งเป็น GNUism ดังนั้นนี่คือ moot แต่ฉันจะเสียหนวดเคราเก่าของฉันถ้าฉันไม่สามารถชี้ให้เห็นได้ว่ารุ่นเก่าบางรุ่นsedไม่อนุญาตให้คุณวางช่องว่างระหว่าง$และd(หรือ โดยทั่วไประหว่างรูปแบบและคำสั่ง)
*(.)เพื่อ glob สำหรับไฟล์ปกติฉันไม่รู้เกี่ยวกับเชลล์อื่น ๆ
คำตอบอื่น ๆ ทั้งหมดมีปัญหาหากไดเรกทอรีมีสิ่งอื่นที่ไม่ใช่ไฟล์ปกติหรือไฟล์ที่มีช่องว่าง / บรรทัดใหม่ในชื่อไฟล์ นี่คือสิ่งที่ทำงานโดยไม่คำนึงถึง:
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
findนี้ถูกเขียนได้อย่างมีประสิทธิภาพมากขึ้นfind $dir -type f -exec sed -i '$d' '{}' '+'.)
-print0ไม่ได้มีอยู่ในคำสั่งเต็มทำไมใส่มันเข้าไปในคำอธิบาย?
-depth 0ไม่ทำงาน (findutils 4.4.2) -maxdepth 1มันควรจะเป็น
-execแต่แล้วจำ
การใช้ 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})
${#length}จะไม่ทำงานเนื่องจากมีการนับอักขระไม่ใช่ไบต์และ$(...)จะลบอักขระบรรทัดใหม่ที่มีหางซึ่ง${#...}จะปิดโดยหนึ่งแม้ว่าอักขระทั้งหมดจะเป็นไบต์เดียว
วิธีการพกพามากขึ้น:
for f in ./*
do
test -f "$f" && ed -s "$f" <<\IN
d
w
q
IN
done
ฉันไม่คิดว่านี่จะต้องมีคำอธิบายใด ๆ ... ยกเว้นอาจเป็นไปได้ว่าในกรณีนี้dเป็นเช่นเดียวกัน$dเนื่องจากedโดยค่าเริ่มต้นเลือกบรรทัดสุดท้าย
สิ่งนี้จะไม่ค้นหาซ้ำและจะไม่ประมวลผลไฟล์ที่ซ่อนอยู่ (aka dotfiles)
หากคุณต้องการแก้ไขเหล่านั้นก็จะเห็นวิธีจับคู่ * กับไฟล์ที่ซ่อนอยู่ในไดเรกทอรีด้วย
[[]]ไป[]จะเป็นไปตาม POSIX อย่างสมบูรณ์ ( [[ ... ]]คือ Bashism)
[[จะไม่ใช่ bashism )
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 {} +
ดูเพิ่มเติมที่: