Saya ingin mendapatkan nama file (tanpa ekstensi) dan ekstensi secara terpisah.
Solusi terbaik yang saya temukan sejauh ini adalah:
NAME=`echo "$FILE" | cut -d'.' -f1`
EXTENSION=`echo "$FILE" | cut -d'.' -f2`
Ini salah karena tidak berfungsi jika nama file berisi banyak .
karakter. Jika, katakanlah, saya punya a.b.js
, itu akan mempertimbangkan a
dan b.js
, bukannya a.b
dan js
.
Ini dapat dengan mudah dilakukan dengan Python
file, ext = os.path.splitext(path)
tapi saya lebih suka untuk tidak menyalakan interpreter Python hanya untuk ini, jika memungkinkan.
Ada ide yang lebih baik?
extension="{$filename##*.}"
seperti yang saya lakukan untuk sementara waktu! Pindahkan bagian$
luar keriting: Kanan:extension="${filename##*.}"
os.path.splitext
seperti di atas sebagai gantinya ...Jawaban:
Pertama, dapatkan nama file tanpa path:
Sebagai alternatif, Anda dapat fokus pada '/' terakhir dari jalur alih-alih '.' yang seharusnya berfungsi meskipun Anda memiliki ekstensi file yang tidak dapat diprediksi:
Anda mungkin ingin memeriksa dokumentasinya:
sumber
basename
extension=$([[ "$filename" = *.* ]] && echo ".${filename##*.}" || echo '')
. Catatan bahwa jika perpanjangan adalah hadir, itu akan dikembalikan termasuk awal.
, misalnya,.txt
.Untuk detail lebih lanjut, lihat ekspansi parameter shell di manual Bash.
sumber
dinosaurs.in.tar
dan Anda gzip kedinosaurs.in.tar.gz
:)x.tar.gz
ekstensigz
dan nama filex.tar
itu dia. Tidak ada yang namanya ekstensi ganda. Saya cukup yakin boost :: filesystem menanganinya seperti itu. (path split, change_extension ...) dan perilakunya didasarkan pada python jika saya tidak salah.Biasanya Anda sudah tahu ekstensi, jadi Anda mungkin ingin menggunakan:
sebagai contoh:
dan kita dapatkan
sumber
basename
yang cukup membuka mata, baik jenis sir / madam :).zip
atau.ZIP
. Apakah ada cara Anda bisa melakukan sesuatu sepertibasename $file {.zip,.ZIP}
?Anda dapat menggunakan keajaiban ekspansi parameter POSIX:
Ada peringatan bahwa jika nama file Anda berbentuk
./somefile.tar.gz
makaecho ${FILENAME%%.*}
rakus akan menghapus pertandingan terpanjang ke.
dan Anda akan memiliki string kosong.(Anda bisa mengatasinya dengan variabel sementara:
)
Situs ini menjelaskan lebih lanjut.
sumber
cut
tidak memiliki--complement
dansed
tidak memiliki-r
.Tampaknya tidak berfungsi jika file tidak memiliki ekstensi, atau tanpa nama file. Inilah yang saya gunakan; hanya menggunakan builtin dan menangani lebih banyak (tetapi tidak semua) nama file patologis.
Dan inilah beberapa testcases:
sumber
dir="${fullpath:0:${#fullpath} - ${#filename}}"
saya sudah sering melihatdir="${fullpath%$filename}"
. Lebih mudah untuk menulis. Tidak yakin apakah ada perbedaan kecepatan nyata atau gotcha.which bash
->/bin/bash
; mungkin itu distro kamu?Anda bisa menggunakannya
basename
.Contoh:
Anda perlu menyediakan basename dengan ekstensi yang akan dihapus, namun jika Anda selalu mengeksekusi
tar
dengan-z
maka Anda tahu ekstensi akan.tar.gz
.Ini harus melakukan apa yang Anda inginkan:
sumber
cd $(basename $1 .tar.gz)
berfungsi untuk file .gz. Tetapi dalam pertanyaan yang dia sebutkanArchive files have several extensions: tar.gz, tat.xz, tar.bz2
berfungsi dengan baik, jadi Anda bisa menggunakan:
Omong-omong, perintahnya bekerja sebagai berikut.
Perintah untuk
NAME
mengganti"."
karakter yang diikuti oleh sejumlah non-"."
karakter hingga akhir baris, tanpa apa-apa (yaitu, menghapus semua dari akhir"."
hingga akhir baris, termasuk). Ini pada dasarnya adalah substitusi non-serakah menggunakan tipu daya regex.Perintah untuk
EXTENSION
mengganti sejumlah karakter diikuti oleh"."
karakter di awal baris, tanpa apa-apa (yaitu, menghapus semua dari awal baris ke titik akhir, termasuk). Ini adalah pengganti serakah yang merupakan tindakan default.sumber
sed 's,\.[^\.]*$,,'
untuk nama, dansed 's,.*\.,., ;t ;g'
untuk ekstensi (menggunakan atipikaltest
danget
perintah, bersama dengansubstitute
perintah khas ).Mellen menulis dalam komentar di posting blog:
Menggunakan Bash, ada juga
${file%.*}
untuk mendapatkan nama file tanpa ekstensi dan${file##*.}
untuk mendapatkan ekstensi itu sendiri. Itu adalah,Output:
sumber
Tidak perlu repot dengan
awk
ataused
atau bahkanperl
untuk tugas sederhana ini. Adaos.path.splitext()
solusi Bash murni, -compatible yang hanya menggunakan ekspansi parameter.Implementasi Referensi
Dokumentasi
os.path.splitext(path)
:Kode python:
Implementasi Bash
Menghormati periode terkemuka
Mengabaikan periode memimpin
Tes
Berikut ini adalah kasus uji untuk penerapan Periode mengabaikan terkemuka , yang harus cocok dengan implementasi referensi Python pada setiap input.
Hasil tes
Semua tes lulus.
sumber
text.tar.gz
seharusnyatext
dan ekstensi menjadi.tar.gz
os.path.splitext
di Python. Apakah implementasi itu masuk akal untuk input yang mungkin kontroversial adalah topik lain."$root"
)? Apa yang bisa terjadi jika mereka dihilangkan? (Saya tidak dapat menemukan dokumentasi tentang masalah ini.) Juga bagaimana ini menangani nama file dengan*
atau?
di dalamnya?*
dan?
tidak istimewa. Jadi dua bagian dari pertanyaan saya saling menjawab. Apakah saya benar bahwa ini tidak didokumentasikan? Atau apakah ini seharusnya dipahami dari fakta bahwa kutipan menonaktifkan ekspansi glob secara umum?root="${path#?}";root="${path::1}${root%.*}"
- lalu lanjutkan sama untuk mengekstrak ekstensi.Anda bisa menggunakan
cut
perintah untuk menghapus dua ekstensi terakhir (".tar.gz"
bagian):Seperti dicatat oleh Clayton Hughes dalam komentar, ini tidak akan berfungsi untuk contoh aktual dalam pertanyaan. Jadi sebagai alternatif saya usulkan menggunakan
sed
dengan ekspresi reguler yang diperluas, seperti ini:Ia bekerja dengan menghapus dua ekstensi terakhir (alfanumerik) tanpa syarat.
[Diperbarui lagi setelah komentar dari Anders Lindahl]
sumber
$
untuk memeriksa bahwa ekstensi yang cocok ada di akhir nama file. Kalau tidak, nama file sepertii.like.tar.gz.files.tar.bz2
mungkin menghasilkan hasil yang tidak terduga.sed
urutan rantai. Bahkan dengan$
di akhir nama file sepertimpc-1.0.1.tar.bz2.tar.gz
akan menghapus keduanya.tar.gz
dan kemudian.tar.bz2
.Berikut adalah beberapa saran alternatif (kebanyakan dalam
awk
), termasuk beberapa kasus penggunaan lanjutan, seperti mengekstraksi nomor versi untuk paket perangkat lunak.Semua kasing menggunakan jalur lengkap asli sebagai input, tanpa tergantung pada hasil antara.
sumber
The jawaban diterima bekerja dengan baik dalam khas kasus , tetapi gagal di tepi kasus , yaitu:
extension=${filename##*.}
kembalikan nama file input daripada string kosong.extension=${filename##*.}
tidak termasuk inisial.
, bertentangan dengan konvensi..
tidak akan bekerja untuk nama file tanpa akhiran.filename="${filename%.*}"
akan menjadi string kosong, jika nama file input dimulai dengan.
dan tidak mengandung.
karakter lebih lanjut (misalnya,.bash_profile
) - bertentangan dengan konvensi.---------
Dengan demikian, kompleksitas solusi yang kuat yang mencakup semua kasus tepi memerlukan fungsi - lihat definisi di bawah ini; itu dapat mengembalikan semua komponen jalan .
Contoh panggilan:
Perhatikan bahwa argumen setelah jalur input dipilih secara bebas, nama variabel posisional .
Untuk melewati variabel yang tidak menarik yang datang sebelum itu, tentukan
_
(untuk menggunakan variabel yang dibuang$_
) atau''
; mis., untuk mengekstrak akar nama file dan ekstensi saja, gunakansplitPath '/etc/bash.bashrc' _ _ fnameroot extension
.Kode uji yang menjalankan fungsi:
Output yang diharapkan - perhatikan kasus tepi:
.
( tidak dianggap sebagai akhiran akhiran)/
(trailing/
diabaikan).
dikembalikan sebagai jalur induk).
-prefixed token (hanya yang terakhir dianggap suffix):sumber
Solusi terkecil dan paling sederhana (dalam satu baris) adalah:
sumber
echo
. Secara umum,echo $(command)
lebih baik ditulis secara sederhanacommand
kecuali Anda secara spesifik memerlukan shell untuk melakukan tokenization whitespace dan ekspansi wildcard pada output daricommand
sebelum menampilkan hasilnya. Kuis: apa hasilnyaecho $(echo '*')
(dan jika itu yang benar-benar Anda inginkan, Anda benar-benar hanya menginginkanecho *
).echo
perintah sama sekali. Saya hanya menggunakannya untuk menunjukkan hasilfoo
yang muncul di baris ke-3 sebagai hasil dari baris ke-2.basename "${file%.*}"
akan melakukan hal yang sama; Anda menggunakan substitusi perintah untuk menangkap outputnya, hanya untukecho
output yang sama segera. (Tanpa mengutip, hasilnya secara nominal berbeda; tapi itu hampir tidak relevan, apalagi fitur, di sini.)basename "$file" .txt
menghindari kerumitan substitusi parameter.Saya pikir jika Anda hanya perlu nama file, Anda dapat mencoba ini:
Dan itu semua = D.
sumber
Anda dapat memaksa memotong untuk menampilkan semua bidang dan yang berikutnya menambahkan
-
ke nomor bidang.Jadi jika FILE adalah
eth0.pcap.gz
, EXTENSION akan menjadipcap.gz
Dengan menggunakan logika yang sama, Anda juga dapat mengambil nama file menggunakan '-' dengan memotong sebagai berikut:
Ini berfungsi bahkan untuk nama file yang tidak memiliki ekstensi.
sumber
Pengenalan file ajaib
Selain banyak jawaban bagus untuk pertanyaan Stack Overflow ini, saya ingin menambahkan:
Di Linux dan unixen lainnya, ada perintah ajaib bernama
file
, yang melakukan deteksi tipe file dengan menganalisis beberapa byte pertama file. Ini adalah alat yang sangat lama, awalnya digunakan untuk server cetak (jika tidak dibuat untuk ... Saya tidak yakin tentang itu).Ekstensi standar dapat ditemukan di
/etc/mime.types
(di desktop Debian GNU / Linux. Lihatman file
danman mime.types
. Mungkin Anda harus menginstalfile
utilitas danmime-support
paket):Anda dapat membuat pestaberfungsi untuk menentukan ekstensi yang tepat. Ada sedikit (tidak sempurna) sampel:
Fungsi ini dapat mengatur variabel Bash yang dapat digunakan nanti:
(Ini terinspirasi dari jawaban benar @Petesh):
sumber
Ok jadi jika saya mengerti dengan benar, masalahnya di sini adalah bagaimana mendapatkan nama dan ekstensi penuh dari file yang memiliki banyak ekstensi, misalnya
stuff.tar.gz
,.Ini bekerja untuk saya:
Ini akan memberi Anda
stuff
nama file dan.tar.gz
ekstensi. Ini berfungsi untuk sejumlah ekstensi, termasuk 0. Semoga ini membantu bagi siapa pun yang memiliki masalah yang sama =)sumber
os.path.splitext
, yang diinginkan OP) adalah('stuff.tar', '.gz')
.Saya menggunakan skrip berikut
sumber
Ini melayani beberapa titik dan spasi dalam nama file, namun jika tidak ada ekstensi itu mengembalikan nama file itu sendiri. Mudah untuk diperiksa; hanya menguji nama file dan ekstensi menjadi sama.
Tentu metode ini tidak berfungsi untuk file .tar.gz. Namun itu bisa ditangani dalam proses dua langkah. Jika ekstensi gz maka periksa lagi untuk melihat apakah ada juga ekstensi tar.
sumber
Cara mengekstrak nama file dan ekstensi pada ikan :
Peringatan: Membagi pada titik terakhir, yang bekerja dengan baik untuk nama file dengan titik-titik di dalamnya, tetapi tidak baik untuk ekstensi dengan titik-titik di dalamnya. Lihat contoh di bawah ini.
Pemakaian:
Mungkin ada cara yang lebih baik untuk melakukan ini. Silakan mengedit jawaban saya untuk memperbaikinya.
Jika ada set ekstensi terbatas yang akan Anda hadapi dan Anda tahu semuanya, coba ini:
Ini tidak memiliki peringatan sebagai contoh pertama, tetapi Anda harus menangani setiap kasus sehingga bisa lebih membosankan tergantung pada berapa banyak ekstensi yang Anda harapkan.
sumber
Berikut ini adalah kode dengan AWK . Itu bisa dilakukan dengan lebih sederhana. Tapi saya tidak pandai dalam AWK.
sumber
split()
.awk -F / '{ n=split($2, a, "."); print a[n] }' uses
/ `sebagai pembatas tingkat atas tetapi kemudian membelah bidang kedua.
dan mencetak elemen terakhir dari array baru.Cukup gunakan
${parameter%word}
Dalam kasus Anda:
Jika Anda ingin mengujinya, semua pekerjaan berikut, dan cukup hapus ekstensi:
sumber
=
tanda - tanda.Membangun dari jawaban Petesh , jika hanya nama file yang dibutuhkan, kedua jalur dan ekstensi dapat dilucuti dalam satu baris,
sumber
filename="$(basename "${fullname%.*}")"
basename
adalah opsional, tetapi menentukan ekstensi untuk dihapus. Substitusi mungkin masih berguna tetapi mungkinbasename
sebenarnya tidak, karena Anda dapat benar-benar melakukan semua penggantian ini dengan shell bawaan.Sebagian besar didasarkan off dari sangat baik @ mklement0 ini, dan penuh sesak acak, berguna bashisms - serta jawaban lain untuk ini / pertanyaan lain / "yang sialan internet" ... aku membungkus semuanya dalam sedikit, sedikit lebih dipahami, fungsi yang dapat digunakan kembali untuk saya (atau Anda)
.bash_profile
yang mengurus apa (saya anggap) harus menjadi versi yang lebih kuat daridirname
/basename
/ apa yang sudah Anda ..Contoh penggunaan ...
sumber
$IFS
sama sekali (dan jika ya, Anda dapat menggunakanlocal
untuk melokalisasi efek pengaturannya). - Lebih baik menggunakanlocal
variabel. - Pesan kesalahan Anda harus di-outputstderr
, bukanstdout
(digunakan1>&2
), dan Anda harus mengembalikan kode keluar yang tidak nol. - Lebih baik untuk mengubah namafullname
menjadibasename
(yang pertama menyarankan jalur dengan komponen dir). -name
tanpa syarat menambahkan.
(periode), bahkan jika aslinya tidak punya. Anda bisa menggunakanbasename
utilitas, tetapi perhatikan bahwa ia mengabaikan terminating/
.Jawaban sederhana:
Untuk memperluas jawaban variabel POSIX , perhatikan bahwa Anda dapat melakukan pola yang lebih menarik. Jadi untuk kasus yang dirinci di sini, Anda bisa melakukan ini:
Itu akan memotong kejadian terakhir .tar. <sesuatu> .
Lebih umum, jika Anda ingin menghapus kejadian terakhir. <sesuatu> . <sesuatu-selain> lalu
harus bekerja dengan baik.
Tautan jawaban di atas tampaknya sudah mati. Berikut adalah penjelasan yang bagus tentang sekelompok manipulasi string yang dapat Anda lakukan langsung di Bash, dari TLDP .
sumber
Jika Anda juga ingin mengizinkan ekstensi kosong , ini adalah yang terpendek yang dapat saya buat:
Baris pertama menjelaskan: Ini cocok dengan PATH.EXT atau APA SAJA dan menggantikannya dengan EXT. Jika APA SAJA dicocokkan, grup ext tidak ditangkap.
sumber
Ini adalah satu-satunya yang bekerja untuk saya:
Ini juga dapat digunakan dalam interpolasi string, tetapi sayangnya Anda harus mengatur
base
terlebih dahulu.sumber
Berikut adalah algoritma yang saya gunakan untuk menemukan nama dan ekstensi file ketika saya menulis skrip Bash untuk membuat nama-nama unik ketika nama-nama bertentangan dengan sehubungan dengan casing.
Uji coba.
FYI: Program transliterasi lengkap dan lebih banyak test case dapat ditemukan di sini: https://www.dropbox.com/s/4c6m0f2e28a1vxf/avoid-clashes-code.zip?dl=0
sumber
extension=$([[ "$theFileName" == *.* ]] && echo ".${theFileName##*.}" || echo '')
Menggunakan file contoh
/Users/Jonathan/Scripts/bash/MyScript.sh
, kode ini:akan menghasilkan
${ME}
menjadiMyScript
dan${MY_EXT}
menjadi.sh
:Naskah:
Beberapa tes:
sumber
basename
, mungkin, berlebihan.Dari jawaban di atas, oneliner terpendek ke meniru Python
anggap file Anda benar-benar memiliki ekstensi, adalah
sumber
EXT
jadi ini adalah kura-kura sepanjang jalan. (Juga, Anda harus menghindari semua huruf besar untuk nama variabel pribadi Anda; mereka dicadangkan untuk variabel sistem.)