วิธีพิมพ์อักขระตัวสุดท้ายของไฟล์


8

ใน Unix ใช้คำสั่งง่าย ๆ เช่น sed มีวิธีพิมพ์อักขระตัวสุดท้ายของไฟล์หรือไม่?


คุณสนใจหรือไม่ว่าอักขระตัวสุดท้ายเป็น "พิมพ์ได้" จริงหรือไม่? นั่นคือถ้าตัวละครตัวสุดท้ายเป็นบรรทัดใหม่จะตกลงไหม?
DaveParillo

คำตอบ:


26

tail เป็นเครื่องมือที่เหมาะสมไม่ใช่ sed

tail -c 1 filename

3
+1 - สำหรับไฟล์ข้อความหากคุณต้องการตัวอักษรตัวสุดท้ายก่อนขึ้นบรรทัดใหม่เพียงแค่แทนที่ 1 ด้วย 2
mouviciel

7

tail -c 2 fileควรทำมัน ควรเป็น -c 1 ในทางทฤษฎี แต่การฝึกฝนพิสูจน์ว่าฉันผิด

แก้ไข: หากไฟล์ของคุณมีอักขระสิ้นสุดบรรทัดที่คุณต้องการละเว้นมันเป็น 2 1


บางทีไฟล์ทดสอบของคุณมีการขึ้นบรรทัดใหม่ในตอนท้าย
Delan Azabani

มันมีหนึ่งอย่างแน่นอน

ฉันไม่เคยเห็นไฟล์ที่มีตัวละครหมดอายุ
Paul R

1
@PaulR บางทีมันอาจเป็นแค่ไฟล์ที่ยาวมากเมื่อคุณมาถึงจุดสุดท้ายคุณก็ตายแล้ว: D
рüффп

4

ลองสิ่งนี้ cat filename | tail -c -1


4
ครั้งแรกที่ฉันเคยเห็น "คำตอบของคุณต้องมีอย่างน้อย 30 ตัวอักษร"

3
การใช้ท่อในทางที่ผิดเหมือน ๆ กับในcat foo grep bar
vtest

@vtest: ข้อผิดพลาดเหล่านั้นคืออะไร?
Eclipse

@vtest บางทีคำว่า "ลองนี่tail -c -1 filename" อาจเป็นคำตอบที่สั้นเกินไป (น้อยกว่า 30 ตัวอักษร) ดังนั้นจึงcat |มีการเพิ่มเพิ่มเติมเพื่อยืดระยะเวลา ;)
ulidtko

@ulidtko: ฉันแสดงความคิดเห็นเกี่ยวกับสูตรไม่ได้อยู่ในความยาวของคำตอบ
vtest

0

สิ่งนี้ควรใช้งานได้หากบรรทัดสุดท้ายไม่ว่างเปล่า:

sed -n '$s/.*\(.\)$/\1/p' file


0

เพื่อประโยชน์ของผู้อ่าน:

ปัญหา

มีความเข้าใจผิดบางอย่างที่นี่ ในขณะที่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ตัวแปรกับไฟล์ที่มีเนื้อหาผิด

ใช่เป็นเรื่องยาก แต่บางครั้งก็จำเป็น

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