Diberikan jalur file string seperti /foo/fizzbuzz.bar
, bagaimana saya menggunakan bash untuk mengekstrak hanya fizzbuzz
bagian dari string tersebut?
Diberikan jalur file string seperti /foo/fizzbuzz.bar
, bagaimana saya menggunakan bash untuk mengekstrak hanya fizzbuzz
bagian dari string tersebut?
Jawaban:
Inilah cara melakukannya dengan operator # dan% di Bash.
$ x="/foo/fizzbuzz.bar"
$ y=${x%.bar}
$ echo ${y##*/}
fizzbuzz
${x%.bar}
bisa juga ${x%.*}
untuk menghapus semuanya setelah titik atau ${x%%.*}
untuk menghapus semuanya setelah titik pertama.
Contoh:
$ x="/foo/fizzbuzz.bar.quux"
$ y=${x%.*}
$ echo $y
/foo/fizzbuzz.bar
$ y=${x%%.*}
$ echo $y
/foo/fizzbuzz
Dokumentasi dapat ditemukan di manual Bash . Cari ${parameter%word}
dan ${parameter%%word}
ikuti bagian pencocokan porsi.
lihat perintah basename:
NAME="$(basename /foo/fizzbuzz.bar .bar)"
Menggunakan nama bas saya menggunakan yang berikut untuk mencapai ini:
for file in *; do
ext=${file##*.}
fname=`basename $file $ext`
# Do things with $fname
done;
Ini tidak memerlukan pengetahuan apriori tentang ekstensi file dan berfungsi bahkan ketika Anda memiliki nama file yang memiliki titik-titik dalam nama file itu (di depan ekstensi itu); itu memang membutuhkan program basename
, tetapi ini adalah bagian dari GNU coreutils sehingga harus dikirimkan dengan distro apa pun.
fname=`basename $file .$ext`
Cara bash murni:
~$ x="/foo/bar/fizzbuzz.bar.quux.zoom";
~$ y=${x/\/*\//};
~$ echo ${y/.*/};
fizzbuzz
Fungsi ini dijelaskan pada man bash di bawah "Ekspansi Parameter". Cara non bash berlimpah: awk, perl, sed dan sebagainya.
EDIT: Bekerja dengan titik-titik di akhiran file dan tidak perlu tahu akhiran (ekstensi), tetapi tidak bekerja dengan titik-titik dalam nama itu sendiri.
Fungsi nama dasar dan dirname adalah yang Anda cari:
mystring=/foo/fizzbuzz.bar
echo basename: $(basename "${mystring}")
echo basename + remove .bar: $(basename "${mystring}" .bar)
echo dirname: $(dirname "${mystring}")
Memiliki output:
basename: fizzbuzz.bar
basename + remove .bar: fizzbuzz
dirname: /foo
mystring=$1
daripada nilai konstan saat ini (yang akan menekan beberapa peringatan, memastikan tidak mengandung spasi / karakter glob / dll), dan mengatasi masalah itu menemukan?
echo "basename: $(basename "$mystring")"
- dengan cara itu jika mystring='/foo/*'
Anda tidak mendapatkan *
diganti dengan daftar file di direktori saat ini setelah basename
selesai.
Menggunakan basename
asumsi bahwa Anda tahu apa ekstensi file itu, bukan?
Dan saya percaya bahwa berbagai saran ekspresi reguler tidak mengatasi nama file yang mengandung lebih dari satu "."
Berikut ini tampaknya mengatasi titik ganda. Oh, dan nama file yang berisi "/" sendiri (hanya untuk iseng)
Mengutip Pascal, "Maaf skrip ini terlalu panjang. Saya tidak punya waktu untuk membuatnya lebih pendek"
#!/usr/bin/perl
$fullname = $ARGV[0];
($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
print $basename . "\n";
Selain sintaks konforman POSIX yang digunakan dalam jawaban ini ,
basename string [suffix]
seperti dalam
basename /foo/fizzbuzz.bar .bar
GNUbasename
mendukung sintaks lain:
basename -s .bar /foo/fizzbuzz.bar
dengan hasil yang sama. Perbedaan dan keuntungannya adalah yang -s
menyiratkan -a
, yang mendukung banyak argumen:
$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar
Ini bahkan dapat membuat nama file aman dengan memisahkan output dengan NUL byte menggunakan -z
opsi, misalnya untuk file-file ini yang mengandung karakter kosong, baris baru dan gumpalan (dikutip oleh ls
):
$ ls has*
'has'$'\n''newline.bar' 'has space.bar' 'has*.bar'
Membaca ke dalam array:
$ readarray -d $'\0' arr < <(basename -zs .bar has*)
$ declare -p arr
declare -a arr=([0]=$'has\nnewline' [1]="has space" [2]="has*")
readarray -d
membutuhkan Bash 4.4 atau yang lebih baru. Untuk versi yang lebih lama, kita harus mengulang:
while IFS= read -r -d '' fname; do arr+=("$fname"); done < <(basename -zs .bar has*)
Jika Anda tidak dapat menggunakan nama bas seperti yang disarankan dalam posting lain, Anda selalu dapat menggunakan sed. Berikut ini adalah contoh (jelek). Ini bukan yang terbaik, tetapi bekerja dengan mengekstraksi string yang diinginkan dan mengganti input dengan string yang diinginkan.
echo '/foo/fizzbuzz.bar' | sed 's|.*\/\([^\.]*\)\(\..*\)$|\1|g'
Yang akan memberi Anda output
fizzbuzz
Waspadalah terhadap solusi perl yang disarankan: ia menghilangkan apa pun setelah titik pertama.
$ echo some.file.with.dots | perl -pe 's/\..*$//;s{^.*/}{}'
some
Jika Anda ingin melakukannya dengan perl, ini berfungsi:
$ echo some.file.with.dots | perl -pe 's/(.*)\..*$/$1/;s{^.*/}{}'
some.file.with
Tetapi jika Anda menggunakan Bash, solusi dengan y=${x%.*}
(atau basename "$x" .ext
jika Anda tahu ekstensi) jauh lebih sederhana.
Nama dasarnya melakukan itu, menghilangkan path. Ini juga akan menghapus akhiran jika diberikan dan jika cocok dengan akhiran file tetapi Anda harus tahu akhiran untuk diberikan kepada perintah. Kalau tidak, Anda dapat menggunakan mv dan mencari tahu apa nama baru itu dengan cara lain.
Menggabungkan jawaban berperingkat teratas dengan jawaban peringkat kedua untuk mendapatkan nama file tanpa path lengkap:
$ x="/foo/fizzbuzz.bar.quux"
$ y=(`basename ${x%%.*}`)
$ echo $y
fizzbuzz
${parameter%word}
dan${parameter%%word}
di bagian pencocokan porsi.