เมื่อกำหนดเส้นทางของไฟล์สตริงเช่น/foo/fizzbuzz.bar
ฉันจะใช้ bash เพื่อแยกfizzbuzz
ส่วนของสตริงดังกล่าวได้อย่างไร
เมื่อกำหนดเส้นทางของไฟล์สตริงเช่น/foo/fizzbuzz.bar
ฉันจะใช้ bash เพื่อแยกfizzbuzz
ส่วนของสตริงดังกล่าวได้อย่างไร
คำตอบ:
ต่อไปนี้เป็นวิธีดำเนินการกับตัวดำเนินการ # และ% ใน Bash
$ x="/foo/fizzbuzz.bar"
$ y=${x%.bar}
$ echo ${y##*/}
fizzbuzz
${x%.bar}
อาจเป็นการ${x%.*}
ลบทุกอย่างหลังจากจุดหรือ${x%%.*}
ลบทุกอย่างหลังจากจุดแรก
ตัวอย่าง:
$ x="/foo/fizzbuzz.bar.quux"
$ y=${x%.*}
$ echo $y
/foo/fizzbuzz.bar
$ y=${x%%.*}
$ echo $y
/foo/fizzbuzz
เอกสารที่สามารถพบได้ในคู่มือการทุบตี ค้นหา${parameter%word}
และ${parameter%%word}
ต่อท้ายส่วนการจับคู่ส่วน
ดูที่คำสั่ง basename:
NAME="$(basename /foo/fizzbuzz.bar .bar)"
Pure ทุบตีทำในสองการดำเนินการแยก:
ลบเส้นทางออกจาก path-string:
path=/foo/bar/bim/baz/file.gif
file=${path##*/}
#$file is now 'file.gif'
ลบนามสกุลออกจาก path-string:
base=${file%.*}
#${base} is now 'file'.
ใช้ basename ฉันใช้ต่อไปนี้เพื่อให้บรรลุนี้
for file in *; do
ext=${file##*.}
fname=`basename $file $ext`
# Do things with $fname
done;
สิ่งนี้ไม่จำเป็นต้องมีความรู้เบื้องต้นเกี่ยวกับนามสกุลไฟล์และใช้งานได้แม้ว่าคุณจะมีชื่อไฟล์ที่มีจุดอยู่ในชื่อไฟล์ (หน้านามสกุลของมัน) มันต้องการโปรแกรมbasename
แต่นี่เป็นส่วนหนึ่งของ coreutils ของ GNU ดังนั้นจึงควรจัดส่งด้วย distro ใด ๆ
fname=`basename $file .$ext`
วิธีทุบตีบริสุทธิ์:
~$ x="/foo/bar/fizzbuzz.bar.quux.zoom";
~$ y=${x/\/*\//};
~$ echo ${y/.*/};
fizzbuzz
ฟังก์ชั่นนี้มีการอธิบายเกี่ยวกับการทุบตีคนภายใต้ "การขยายพารามิเตอร์" วิธีการทุบตีที่ไม่อุดมสมบูรณ์: awk, perl, sed และอื่น ๆ
แก้ไข: ทำงานร่วมกับจุดในคำต่อท้ายไฟล์และไม่จำเป็นต้องรู้คำต่อท้าย (ส่วนขยาย) แต่ไม่ได้ทำงานกับจุดในชื่อตัวเอง
ฟังก์ชัน basename และ dirname คือสิ่งที่คุณต้องการ:
mystring=/foo/fizzbuzz.bar
echo basename: $(basename "${mystring}")
echo basename + remove .bar: $(basename "${mystring}" .bar)
echo dirname: $(dirname "${mystring}")
มีเอาต์พุต:
basename: fizzbuzz.bar
basename + remove .bar: fizzbuzz
dirname: /foo
mystring=$1
แทนที่จะเป็นค่าคงที่ในปัจจุบัน (ซึ่งจะระงับคำเตือนหลายคำ พบ?
echo "basename: $(basename "$mystring")"
- หากmystring='/foo/*'
คุณไม่ได้รับการ*
แทนที่ด้วยรายการไฟล์ในไดเรกทอรีปัจจุบันหลังจาก basename
เสร็จสิ้น
การใช้ basename
จะถือว่าคุณรู้ว่าส่วนขยายของไฟล์คืออะไรใช่ไหม
และฉันเชื่อว่าคำแนะนำการแสดงออกปกติต่าง ๆ ไม่ได้รับมือกับชื่อไฟล์ที่มีมากกว่าหนึ่ง "."
ต่อไปนี้ดูเหมือนว่าจะรับมือกับจุดสองจุด โอ้และชื่อไฟล์ที่มี "/" ตัวเอง (เพียงเพื่อความบันเทิง)
ในการถอดความภาษาปาสคาล "ขออภัยสคริปต์นี้ยาวมากฉันไม่มีเวลาที่จะทำให้สั้นลง"
#!/usr/bin/perl
$fullname = $ARGV[0];
($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
print $basename . "\n";
นอกจากนี้ยังมีไวยากรณ์ที่สอดคล้องกับ POSIXใช้ในคำตอบนี้ ,
basename string [suffix]
เช่นเดียวกับใน
basename /foo/fizzbuzz.bar .bar
GNUbasename
รองรับไวยากรณ์อื่น:
basename -s .bar /foo/fizzbuzz.bar
ด้วยผลลัพธ์เดียวกัน ความแตกต่างและความได้เปรียบคือเป็น-s
นัย-a
ซึ่งรองรับหลายอาร์กิวเมนต์:
$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar
สิ่งนี้สามารถสร้างชื่อไฟล์ได้อย่างปลอดภัยโดยแยกเอาท์พุทด้วย NUL ไบต์โดยใช้-z
ตัวเลือกตัวอย่างเช่นไฟล์เหล่านี้ที่มีช่องว่าง, บรรทัดใหม่และตัวอักษรกลม (อ้างอิงโดยls
):
$ ls has*
'has'$'\n''newline.bar' 'has space.bar' 'has*.bar'
การอ่านอาเรย์:
$ readarray -d $'\0' arr < <(basename -zs .bar has*)
$ declare -p arr
declare -a arr=([0]=$'has\nnewline' [1]="has space" [2]="has*")
readarray -d
ต้องการ Bash 4.4 หรือใหม่กว่า สำหรับเวอร์ชั่นเก่าเราต้องวนซ้ำ:
while IFS= read -r -d '' fname; do arr+=("$fname"); done < <(basename -zs .bar has*)
perl -pe 's/\..*$//;s{^.*/}{}'
หากคุณไม่สามารถใช้ basename ตามที่แนะนำในโพสต์อื่น ๆ คุณสามารถใช้ sed ได้ตลอดเวลา นี่คือตัวอย่าง (น่าเกลียด) มันไม่ใช่สิ่งที่ยิ่งใหญ่ที่สุด แต่ทำงานได้โดยการแยกสตริงที่ต้องการและแทนที่อินพุตด้วยสตริงที่ต้องการ
echo '/foo/fizzbuzz.bar' | sed 's|.*\/\([^\.]*\)\(\..*\)$|\1|g'
ซึ่งคุณจะได้รับผลลัพธ์
FizzBuzz
ระวังวิธีการแก้ปัญหา perl ที่แนะนำ: มันจะลบสิ่งใดหลังจากจุดแรก
$ echo some.file.with.dots | perl -pe 's/\..*$//;s{^.*/}{}'
some
หากคุณต้องการทำด้วย perl งานนี้:
$ echo some.file.with.dots | perl -pe 's/(.*)\..*$/$1/;s{^.*/}{}'
some.file.with
แต่ถ้าคุณใช้ Bash โซลูชันที่มีy=${x%.*}
(หรือbasename "$x" .ext
ถ้าคุณรู้ว่าส่วนขยาย) นั้นง่ายกว่ามาก
ชื่อไฟล์ทำเช่นนั้นลบเส้นทาง นอกจากนี้ยังจะลบคำต่อท้ายหากได้รับและหากตรงกับคำต่อท้ายของไฟล์ แต่คุณจะต้องรู้คำต่อท้ายเพื่อให้คำสั่ง มิฉะนั้นคุณสามารถใช้ mv และค้นหาว่าชื่อใหม่ควรมีวิธีอื่นอย่างไร
การรวมคำตอบที่ติดอันดับสูงสุดเข้ากับคำตอบที่ติดอันดับสองเพื่อให้ได้ชื่อไฟล์โดยไม่มีเส้นทางแบบเต็ม:
$ x="/foo/fizzbuzz.bar.quux"
$ y=(`basename ${x%%.*}`)
$ echo $y
fizzbuzz
${parameter%word}
และ${parameter%%word}
ในส่วนการจับคู่ส่วนต่อท้าย