ฉันมีไฟล์ข้อความจำนวนมากในไดเรกทอรีและฉันต้องการลบบรรทัดสุดท้ายของทุกไฟล์ในไดเรกทอรี
ฉันจะทำมันได้อย่างไร
ฉันมีไฟล์ข้อความจำนวนมากในไดเรกทอรีและฉันต้องการลบบรรทัดสุดท้ายของทุกไฟล์ในไดเรกทอรี
ฉันจะทำมันได้อย่างไร
คำตอบ:
หากคุณมีสิทธิ์เข้าถึงvim
คุณสามารถใช้:
for file in ./*
do
if [ -f "${file}" ]
then
vim -c '$d' -c "wq" "${file}"
fi
done
คุณสามารถใช้ oneliner ดีถ้าคุณมี sed
GNU
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 1
find
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 {} +
ดูเพิ่มเติมที่: