ใน Unix ใช้คำสั่งง่าย ๆ เช่น sed มีวิธีพิมพ์อักขระตัวสุดท้ายของไฟล์หรือไม่?
ใน Unix ใช้คำสั่งง่าย ๆ เช่น sed มีวิธีพิมพ์อักขระตัวสุดท้ายของไฟล์หรือไม่?
คำตอบ:
tail เป็นเครื่องมือที่เหมาะสมไม่ใช่ sed
tail -c 1 filename
tail -c 2 file
ควรทำมัน ควรเป็น -c 1 ในทางทฤษฎี แต่การฝึกฝนพิสูจน์ว่าฉันผิด
แก้ไข: หากไฟล์ของคุณมีอักขระสิ้นสุดบรรทัดที่คุณต้องการละเว้นมันเป็น 2 1
ลองสิ่งนี้ cat filename | tail -c -1
tail -c -1 filename
" อาจเป็นคำตอบที่สั้นเกินไป (น้อยกว่า 30 ตัวอักษร) ดังนั้นจึงcat |
มีการเพิ่มเพิ่มเติมเพื่อยืดระยะเวลา ;)
สิ่งนี้ควรใช้งานได้หากบรรทัดสุดท้ายไม่ว่างเปล่า:
sed -n '$s/.*\(.\)$/\1/p' file
ใช้: cat test | sed -e "s/^.*\(.\)$/\1/"
โดยที่ test คือชื่อไฟล์ของคุณ
เพื่อประโยชน์ของผู้อ่าน:
มีความเข้าใจผิดบางอย่างที่นี่ ในขณะที่tail -c1 file
เป็นคำสั่งที่ถูกต้องในการพิมพ์อักขระตัวสุดท้ายของไฟล์สิ่งนี้ไม่สามารถใช้เพื่อตรวจสอบได้อย่างง่ายดายว่าไฟล์จบNL
จากระดับเชลล์หรือไม่เนื่องจากวิธีที่ POSIX-shells แสดงผล:
if last_character="$(tail -c1 "$file")"; then ..
# WRONG! last_character will be empty on NL, as well as on an empty file
ด้วยเหตุผลที่ไม่ทราบสาเหตุบางอย่างของ POSIX จะกำหนดว่าการลาก NL ทั้งหมดต่อท้ายจะถูกปล้นหากคุณจับเอาท์พุทของคำสั่งบางอย่าง โปรดทราบว่าเชลล์สคริปต์เกือบทั้งหมดออกวางกับดักนี้แม้แต่สิ่งที่ง่ายที่สุดเช่น
cd "$(dirname -- "$0")" || exit # Wrong, see: mkdir $'\n'
base="$(basename -- "$0" .sh)" || exit # Wrong, see: mv script.sh $'\n.sh'
ผู้โจมตีอาจใช้ประโยชน์จากมาตรฐาน POSIX นี้โดยใช้ซอฟต์ลิงค์!
โปรดทราบว่าฉันใช้
bash
ไวยากรณ์เวอร์ชัน 4 ที่นี่
คุณสามารถทำสิ่งนี้:
set -o pipefail
if tail -c1 file | { IFS= read -rd '' var; postprocess "$var"; }; then ..
แต่นี่เป็น PITA เนื่องจากการแยกย่อย หรือ
6< <(tail -c1 file) IFS= read -ru6 -d '' var
ซึ่งก็แปลกเหมือนกัน (วิธีการตรวจสอบความล้มเหลวของtail
ที่นี่ ฯลฯ )
แต่สูตรต่อไปนี้ใช้งานได้ปกติ:
var="$(tail -c1 file && echo x)" && var="${var%x}" || error ..
สำหรับคำสั่งที่เพิ่ม NL พิเศษเสมอ (เกือบทุกคำสั่งทำสิ่งนี้เช่นbasename
หรือdirname
) คุณต้องปรับเปลี่ยนสิ่งนี้เป็น:
var="$(basename -- "$file" && echo x)" && var="${var%$'\nx'}" || error ..
#----------------------------------------------------^^^^^^ change
โปรดทราบว่าสูตรนี้ยังใช้งานได้ในสถานการณ์ปกติอื่น ๆ :
# This is only correct here because we only expect a single filename
echo wrong > $'file'
echo right > $'file\n'
wrongname="$(fgrep -lx right file*)" || notfound
correctname="$(fgrep -lx right file* && echo x)" && correctname="${correctname%$'\nx'}" || notfound
wrongname
จะมีfile
ในขณะที่correctname
จะมีfile\n
(ที่\n
ย่อมาจากNL
) เนื่องจากไฟล์ทั้งสองมีอยู่และมีข้อมูลอยู่คุณอาจไม่เห็นข้อผิดพลาดอย่างรวดเร็วว่าคุณใช้wrongname
ตัวแปรกับไฟล์ที่มีเนื้อหาผิด
ใช่เป็นเรื่องยาก แต่บางครั้งก็จำเป็น