ฉันจะได้รับชื่อไฟล์โดยใช้ sed เท่านั้นได้อย่างไร ฉันนี้
out_file=$(echo $in_file|sed "s/\(.*\.\).*/\1mp4/g")
แต่ฉันได้รับเส้นทางเกินไปและฉันต้องการเพียง/root/video.mp4
video.mp4
ฉันจะได้รับชื่อไฟล์โดยใช้ sed เท่านั้นได้อย่างไร ฉันนี้
out_file=$(echo $in_file|sed "s/\(.*\.\).*/\1mp4/g")
แต่ฉันได้รับเส้นทางเกินไปและฉันต้องการเพียง/root/video.mp4
video.mp4
คำตอบ:
basename
จากcoreutils ของ GNU สามารถช่วยคุณทำงานนี้ได้:
$ basename /root/video.mp4
video.mp4
หากคุณรู้จักส่วนขยายของไฟล์อยู่แล้วคุณสามารถเรียกbasename
ใช้โดยใช้ไวยากรณ์basename NAME [SUFFIX]
เพื่อที่จะลบมัน:
$ basename /root/video.mp4 .mp4
video
หรือตัวเลือกอื่นจะตัดทุกอย่างหลังจากจุดสุดท้ายโดยใช้sed
:
$ basename /root/video.old.mp4 | sed 's/\.[^.]*$//'
video.old
ทางออกที่ง่ายที่สุดคือการลบทุกอย่างจนกระทั่งการปรากฏตัวครั้งสุดท้ายของ/
:
echo /root/video.mp4 | sed 's/.*\///'
ใช้วิธีต่อไปนี้:
out_file="${in_file##*/}"
out_file="$(basename $in_file)"
out_file="$(echo $in_file | sed 's=.*/==')"
out_file="$(echo $in_file | awk -F"/" '{ print $NF }')"
PS คุณได้รับสายอักขระเดียวกันเนื่องจากในคำสั่งของคุณ\(.*\.\)
ตรงกับสายอักขระจากจุดเริ่มต้นจนถึงจุด ( /root/video.
) และจากนั้นคุณเพิ่มด้วยตนเอง.mp4
ซึ่งเป็นเหมือนในสายอักขระเดิมของคุณ คุณควรใช้s=.*\([^/]*\)=\1=
แทน
อัปเดต: (อันแรกได้รับการแก้ไขแล้ว)
ในการรับชื่อไฟล์ที่ไม่มีนามสกุลคุณสามารถ:
out_file="$(echo $in_file | sed 's=.*/==;s/\.[^.]*$/.new_ext/')"
out_file="$(echo $in_file | sed 's=\([^/]*\)\.[^./]*$=\1.new_ext=')"
out_file="$(echo $in_file | awk -F"/" '{ gsub (/\.[^/.]*$/,".new_ext",$NF);print $NF }'
my.file.tar.gz
จะมีกรณีขอบเช่นสำหรับไฟล์ชื่อ
sed
awk
แก้ไขแล้ว. ขอขอบคุณ.
หนึ่งในปัจจัยพื้นฐานของการใช้ regex คือรูปแบบนั้นโลภโดยธรรมชาติเมื่อระบุ wild card ในขณะที่คำตอบที่เสนอโดย @uloBasEI เป็นคำตอบที่ใช้งานได้จริง แต่ก็ต้องใช้คำสั่ง basename ด้วย คำถามดั้งเดิมจาก @Shixons ร้องขอวิธีการแก้ปัญหาโดยใช้ sed เท่านั้น
ก่อนดำเนินการต่อจะเป็นประโยชน์เสมอที่จะทราบว่ารุ่นของ sed ใดที่เป็นเป้าหมาย ฉันสมมติว่า BSD (จัดส่งมาพร้อมกับ OSX)
ก่อนอื่นรูปแบบที่เสนอในคำถามเดิมไม่ทำงานเพราะจับทุกอย่างตั้งแต่เริ่มต้นของสตริงอินพุตจนถึงและรวมถึงจุดสุดท้าย หากไม่มีจุดยึดการค้นหานี้จะกลืนทุกอย่างจากซ้ายไปขวา รูปแบบที่ตรงกัน "/ 1" จึงเป็นทุกอย่างจนถึงจุดสุดท้าย แม้แต่ชื่อไฟล์ที่มีหลายจุดจะถูกกลืนทั้งตัว ไม่ใช่ผลลัพธ์ที่ต้องการเลย
ขั้นตอนแรกคือการสร้างกลยุทธ์สำหรับการระบุรูปแบบ ที่นี่คุณต้องการกำจัดทุกอย่างทางด้านซ้ายของชื่อไฟล์ (เราจะจัดการกับส่วนขยายในภายหลัง):
out_file="$(echo $in_file | sed 's/^\(\/.*\/\)*.*/\1/')"
การค้นหาตรงกันจากจุดเริ่มต้นของสตริง มันตรงกับรูปแบบของ "/.*" ศูนย์หรือมากกว่านั้นและลบทุกอย่างในภายหลัง เราพิมพ์รูปแบบที่ตรงกันด้วย "\ 1" เราไม่ได้ค้นหาทั่วโลก เรากำลังค้นหาตั้งแต่เริ่มต้นของสตริงโดยการระบุ ^ anchor
เราได้ความชัดเจนที่ดีขึ้นโดยการเปิดใช้งานตัวเลือก "-E" ดังนั้นเราจึงไม่ต้องหลบเลี่ยงวงเล็บ:
out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*.*/\1/')"
ดังนั้นตอนนี้เรามีส่วนทางด้านซ้าย ลองเพิ่มส่วนทางด้านขวา โปรดทราบว่าเราต้องเก็บส่วนซ้ายไว้เป็นรูปแบบเนื่องจากเป็นวิธีที่เราสามารถระบุได้ว่าจะปรากฏเป็นศูนย์หรือมากกว่านั้น สิ่งที่เราทำตอนนี้คือการเพิ่มรูปแบบสำหรับส่วนทางด้านขวา:
out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)/\2/')"
เราพิมพ์คู่ที่สองเท่านั้นดังนั้นจึงละทิ้งทุกอย่างยกเว้นชื่อไฟล์ แต่เรายังต้องลบส่วนขยายชื่อไฟล์
out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)\..*$/\2/')"
"$" ในตอนท้ายเป็นตัวเลือก
ในที่สุดหากต้องการเพิ่มส่วนขยายใหม่คุณเพียงแก้ไขดังนี้
out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)\..*$/\2.mp4/')"
การเพิ่มประสิทธิภาพเพิ่มเติมคือการทำให้ฟอร์เวิร์ดสแลชแรกเป็นทางเลือกเพื่อจัดการพา ธ ที่เกี่ยวข้อง:
out_file="$(echo $in_file | sed -E 's/^([\/]?.*\/)*(.*)\..*$/\2.mp4/')"
ฉันมาข้ามคำถามนี้โดยเป็นขี้เกียจในขณะที่มองหารูปแบบ sed เพื่อแทนที่basename ฉันกำลังทำงานกับระบบที่ปล้นซึ่งไม่มีคำสั่งนั้นติดตั้งอยู่
sed 's/\.[^.]*$//'
ที่คุณมีจะล้มเหลว (ซ่อนไว้).filename
และ.
และ..
ไดเรกทอรี