Saya ingin mendapatkan nama file (tanpa ekstensi) dan ekstensi secara terpisah.
Solusi terbaik yang saya temukan sejauh ini:
NAME=`echo "$FILE" | cut -d'.' -f1`
EXTENSION=`echo "$FILE" | cut -d'.' -f2`
Ini adalah salah karena itu doesn't bekerja jika nama file yang berisi beberapa .
karakter. Jika, let's mengatakan, saya memiliki a.b.js
, itu akan mempertimbangkan a
dan b.js
, bukan a.b
dan js
.
Hal ini dapat dengan mudah dilakukan di Python dengan
file, ext = os.path.splitext(path)
tapi aku'd sukai tidak untuk api up interpreter Python hanya untuk ini, jika mungkin.
Ide yang lebih baik?
Pertama, mendapatkan nama file tanpa jalan:
filename=$(basename -- "$fullfile")
extension="${filename##*.}"
filename="${filename%.*}"
Atau, anda dapat fokus pada terakhir '/' jalan bukan '.' yang harus bekerja bahkan jika anda telah tidak terduga ekstensi file:
filename="${fullfile##*/}"
Anda mungkin ingin memeriksa dokumentasi :
Anda dapat menggunakan sihir dari POSIX variabel:
bash-3.2$ FILENAME=somefile.tar.gz
bash-3.2$ echo ${FILENAME%%.*}
somefile
bash-3.2$ echo ${FILENAME%.*}
somefile.tar
Ada's sebuah peringatan bahwa jika anda filename adalah bentuk ./somefile.tar.gz
kemudian echo ${NAMA file%%.*}
akan rakus menghapus terpanjang setara dengan .
dan anda'd memiliki string kosong.
(Anda dapat bekerja di sekitar itu dengan variabel sementara:
FULL_FILENAME=$FILENAME
FILENAME=${FULL_FILENAME##*/}
echo ${FILENAME%%.*}
)
Ini situs menjelaskan lebih lanjut.
${variable%pattern}
Trim the shortest match from the end
${variable##pattern}
Trim the longest match from the beginning
${variable%%pattern}
Trim the longest match from the end
${variable#pattern}
Trim the shortest match from the beginning
Itu doesn't tampaknya bekerja jika file tersebut tidak memiliki ekstensi, atau tidak ada nama berkas. Berikut adalah apa yang saya'm menggunakan; hanya menggunakan builtins dan menangani lebih banyak (tetapi tidak semua) patologis nama file.
#!/bin/bash
for fullpath in "$@"
do
filename="${fullpath##*/}" # Strip longest match of */ from start
dir="${fullpath:0:${#fullpath} - ${#filename}}" # Substring from 0 thru pos of filename
base="${filename%.[^.]*}" # Strip shortest match of . plus at least one non-dot char from end
ext="${filename:${#base} + 1}" # Substring from len of base thru end
if [[ -z "$base" && -n "$ext" ]]; then # If we have an extension and no base, it's really the base
base=".$ext"
ext=""
fi
echo -e "$fullpath:\n\tdir = \"$dir\"\n\tbase = \"$base\"\n\text = \"$ext\""
done
Dan berikut ini adalah beberapa testcases:
$ basename-and-extension.sh / /home/saya/ /home/saya/file /home/saya/file.tar /home/me/file.tar.gz /home/me/.tersembunyi /home/me/.yang tersembunyi.tar /home/saya/.. . /: dir = "/" basis = "" ext = "" /home/saya/: dir = "/home/me/" basis = "" ext = "" /home/saya/file: dir = "/home/me/" basis = "file" ext = "" /home/saya/file.tar: dir = "/home/me/" basis = "file" ext = "ter" /home/me/file.tar.gz: dir = "/home/me/" basis = "berkas.tar" ext = "gz" /home/me/.tersembunyi: dir = "/home/me/" basis = ".tersembunyi" ext = "" /home/me/.yang tersembunyi.tar: dir = "/home/me/" basis = ".tersembunyi" ext = "ter" /home/saya/..: dir = "/home/me/" basis = ".." ext = "" .: dir = "" basis = "." ext = ""
Anda dapat menggunakan nama dasar
.
Contoh:
$ basename foo-bar.tar.gz .tar.gz
foo-bar
Anda perlu untuk memberikan basename dengan ekstensi yang akan dihapus, namun jika anda selalu melaksanakan tar
dengan -z
maka anda tahu ekstensi akan .tar.gz
.
Ini harus melakukan apa yang anda inginkan:
tar -zxvf $1
cd $(basename $1 .tar.gz)
pax> echo a.b.js | sed 's/\.[^.]*$//'
a.b
pax> echo a.b.js | sed 's/^.*\.//'
js
bekerja dengan baik, sehingga anda hanya dapat menggunakan:
pax> FILE=a.b.js
pax> NAME=$(echo "$FILE" | sed 's/\.[^.]*$//')
pax> EXTENSION=$(echo "$FILE" | sed 's/^.*\.//')
pax> echo $NAME
a.b
pax> echo $EXTENSION
js
Perintah, dengan cara kerja sebagai berikut.
Perintah untuk NAMA
pengganti "."
karakter diikuti oleh jumlah non-"."
karakter hingga akhir baris, dengan apa-apa (yaitu, ini akan menghapus segala sesuatu dari akhir "."
ke akhir baris, inklusif). Ini pada dasarnya adalah non-serakah substitusi menggunakan regex tipu daya.
Perintah untuk EKSTENSI
pengganti jumlah karakter diikuti oleh "."
karakter di awal baris, dengan apa-apa (yaitu, ini akan menghapus segala sesuatu dari mulai dari baris ke final dot, inklusif). Ini adalah serakah substitusi yang merupakan default action.
Mellen menulis dalam sebuah komentar pada sebuah posting blog:
Menggunakan Bash, ada juga ${file%.*}
untuk mendapatkan nama file tanpa ekstensi dan ${file##*.}
untuk mendapatkan perpanjangan saja. Artinya,
file="thisfile.txt"
echo "filename: ${file%.*}"
echo "extension: ${file##*.}"
Output:
filename: thisfile
extension: txt
Tidak perlu repot-repot dengan awk
atau sed
atau bahkan perl
untuk tugas sederhana ini. Ada yang murni-Bash, os.jalan.splitext()
-kompatibel solusi yang hanya menggunakan parameter ekspansi.
Dokumentasi dari os.jalan.splitext(path)
:
Membagi path path menjadi sepasang
(akar, ext)
seperti ituakar + ext == path
, dan ext kosong atau dimulai dengan periode dan mengandung paling banyak satu periode. Terkemuka periode pada basename diabaikan;splitext('.cshrc')
mengembalikan('.cshrc', '')
.
Kode Python:
root, ext = os.path.splitext(path)
root="${path%.*}"
ext="${path#"$root"}"
root="${path#.}";root="${path%"$root"}${root%.*}"
ext="${path#"$root"}"
Berikut ini adalah kasus uji untuk Mengabaikan terkemuka periode implementasi, yang harus sesuai dengan Python referensi implementasi pada setiap masukan.
|---------------|-----------|-------|
|path |root |ext |
|---------------|-----------|-------|
|' .txt' |' ' |'.txt' |
|' .txt.txt' |' .txt' |'.txt' |
|' txt' |' txt' |'' |
|'*.txt.txt' |'*.txt' |'.txt' |
|'.cshrc' |'.cshrc' |'' |
|'.txt' |'.txt' |'' |
|'?.txt.txt' |'?.txt' |'.txt' |
|'\n.txt.txt' |'\n.txt' |'.txt' |
|'\t.txt.txt' |'\t.txt' |'.txt' |
|'a b.txt.txt' |'a b.txt' |'.txt' |
|'a*b.txt.txt' |'a*b.txt' |'.txt' |
|'a?b.txt.txt' |'a?b.txt' |'.txt' |
|'a\nb.txt.txt' |'a\nb.txt' |'.txt' |
|'a\tb.txt.txt' |'a\tb.txt' |'.txt' |
|'txt' |'txt' |'' |
|'txt.pdf' |'txt' |'.pdf' |
|'txt.tar.gz' |'txt.tar' |'.gz' |
|'txt.txt' |'txt' |'.txt' |
|---------------|-----------|-------|
Semua tes lulus.
Anda bisa menggunakan cut
perintah untuk menghapus dua ekstensi (the ".tar.gz"
bagian):
$ echo "foo.tar.gz" | cut -d'.' --complement -f2-
foo
Seperti yang dikemukakan oleh Clayton Hughes di komentar, ini tidak akan bekerja untuk contoh aktual dalam pertanyaan. Jadi sebagai alternatif saya mengusulkan menggunakan sed
dengan extended regular expressions, seperti ini:
$ echo "mpc-1.0.1.tar.gz" | sed -r 's/\.[[:alnum:]]+\.[[:alnum:]]+$//'
mpc-1.0.1
Ia bekerja dengan menghapus dua (alpha-numerik) ekstensi tanpa syarat.
[Diperbarui lagi setelah komentar dari Anders Lindahl]
Berikut adalah beberapa saran alternatif (terutama di awk
), termasuk beberapa penggunaan lanjutan kasus, seperti penggalian nomor versi untuk perangkat lunak paket.
f='/path/to/complex/file.1.0.1.tar.gz'
# Filename : 'file.1.0.x.tar.gz'
echo "$f" | awk -F'/' '{print $NF}'
# Extension (last): 'gz'
echo "$f" | awk -F'[.]' '{print $NF}'
# Extension (all) : '1.0.1.tar.gz'
echo "$f" | awk '{sub(/[^.]*[.]/, "", $0)} 1'
# Extension (last-2): 'tar.gz'
echo "$f" | awk -F'[.]' '{print $(NF-1)"."$NF}'
# Basename : 'file'
echo "$f" | awk '{gsub(/.*[/]|[.].*/, "", $0)} 1'
# Basename-extended : 'file.1.0.1.tar'
echo "$f" | awk '{gsub(/.*[/]|[.]{1}[^.]+$/, "", $0)} 1'
# Path : '/path/to/complex/'
echo "$f" | awk '{match($0, /.*[/]/, a); print a[0]}'
# or
echo "$f" | grep -Eo '.*[/]'
# Folder (containing the file) : 'complex'
echo "$f" | awk -F'/' '{$1=""; print $(NF-1)}'
# Version : '1.0.1'
# Defined as 'number.number' or 'number.number.number'
echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?'
# Version - major : '1'
echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f1
# Version - minor : '0'
echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f2
# Version - patch : '1'
echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f3
# All Components : "path to complex file 1 0 1 tar gz"
echo "$f" | awk -F'[/.]' '{$1=""; print $0}'
# Is absolute : True (exit-code : 0)
# Return true if it is an absolute path (starting with '/' or '~/'
echo "$f" | grep -q '^[/]\|^~/'
Semua kasus penggunaan yang asli menggunakan full path sebagai input, tanpa tergantung pada hasil antara.
The jawaban yang diterima bekerja dengan baik di typical kasus, tapi gagal di edge kasus, yaitu:
ekstensi=${nama file##*.}
kembali input filename bukan string kosong.ekstensi=${nama file##*.}
tidak seperti awal .
, bertentangan dengan konvensi..
tidak akan bekerja untuk nama file tanpa akhiran.filename="${nama file%.*}"
akan menjadi string kosong, jika input file yang namanya dimulai dengan .
dan tidak mengandung lanjut .
karakter (misalnya, .bash_profile
) - bertentangan dengan konvensi. Contoh call:
splitPath '/etc/bash.bashrc' dir fname fnameroot suffix
# -> $dir == '/etc'
# -> $fname == 'bash.bashrc'
# -> $fnameroot == 'bash'
# -> $suffix == '.bashrc'
Perhatikan bahwa argumen setelah masukan path yang dipilih secara bebas, posisi variabel names.
Untuk melewatkan variabel tidak minat yang datang sebelum orang-orang yang, menentukan _
(menggunakan variabel $_
) atau''
; misalnya, untuk mengekstrak file root dan ekstensi saja, gunakan splitPath '/etc/bash.bashrc' _ _ fnameroot ekstensi
.
# SYNOPSIS
# splitPath path varDirname [varBasename [varBasenameRoot [varSuffix]]]
# DESCRIPTION
# Splits the specified input path into its components and returns them by assigning
# them to variables with the specified *names*.
# Specify '' or throw-away variable _ to skip earlier variables, if necessary.
# The filename suffix, if any, always starts with '.' - only the *last*
# '.'-prefixed token is reported as the suffix.
# As with `dirname`, varDirname will report '.' (current dir) for input paths
# that are mere filenames, and '/' for the root dir.
# As with `dirname` and `basename`, a trailing '/' in the input path is ignored.
# A '.' as the very first char. of a filename is NOT considered the beginning
# of a filename suffix.
# EXAMPLE
# splitPath '/home/jdoe/readme.txt' parentpath fname fnameroot suffix
# echo "$parentpath" # -> '/home/jdoe'
# echo "$fname" # -> 'readme.txt'
# echo "$fnameroot" # -> 'readme'
# echo "$suffix" # -> '.txt'
# ---
# splitPath '/home/jdoe/readme.txt' _ _ fnameroot
# echo "$fnameroot" # -> 'readme'
splitPath() {
local _sp_dirname= _sp_basename= _sp_basename_root= _sp_suffix=
# simple argument validation
(( $# >= 2 )) || { echo "$FUNCNAME: ERROR: Specify an input path and at least 1 output variable name." >&2; exit 2; }
# extract dirname (parent path) and basename (filename)
_sp_dirname=$(dirname "$1")
_sp_basename=$(basename "$1")
# determine suffix, if any
_sp_suffix=$([[ $_sp_basename = *.* ]] && printf %s ".${_sp_basename##*.}" || printf '')
# determine basename root (filemane w/o suffix)
if [[ "$_sp_basename" == "$_sp_suffix" ]]; then # does filename start with '.'?
_sp_basename_root=$_sp_basename
_sp_suffix=''
else # strip suffix from filename
_sp_basename_root=${_sp_basename%$_sp_suffix}
fi
# assign to output vars.
[[ -n $2 ]] && printf -v "$2" "$_sp_dirname"
[[ -n $3 ]] && printf -v "$3" "$_sp_basename"
[[ -n $4 ]] && printf -v "$4" "$_sp_basename_root"
[[ -n $5 ]] && printf -v "$5" "$_sp_suffix"
return 0
}
test_paths=(
'/etc/bash.bashrc'
'/usr/bin/grep'
'/Users/jdoe/.bash_profile'
'/Library/Application Support/'
'readme.new.txt'
)
for p in "${test_paths[@]}"; do
echo ----- "$p"
parentpath= fname= fnameroot= suffix=
splitPath "$p" parentpath fname fnameroot suffix
for n in parentpath fname fnameroot suffix; do
echo "$n=${!n}"
done
done
Tes kode bahwa latihan fungsi:
test_paths=(
'/etc/bash.bashrc'
'/usr/bin/grep'
'/Users/jdoe/.bash_profile'
'/Library/Application Support/'
'readme.new.txt'
)
for p in "${test_paths[@]}"; do
echo ----- "$p"
parentpath= fname= fnameroot= suffix=
splitPath "$p" parentpath fname fnameroot suffix
for n in parentpath fname fnameroot suffix; do
echo "$n=${!n}"
done
done
Output yang diharapkan - note edge kasus:
.
(not dianggap mulai dari akhiran)/
(akhiran /
diabaikan).
dikembalikan sebagai induk jalan).
-diawali tanda (hanya yang terakhir ini dianggap sebagai akhiran):----- /etc/bash.bashrc
parentpath=/etc
fname=bash.bashrc
fnameroot=bash
suffix=.bashrc
----- /usr/bin/grep
parentpath=/usr/bin
fname=grep
fnameroot=grep
suffix=
----- /Users/jdoe/.bash_profile
parentpath=/Users/jdoe
fname=.bash_profile
fnameroot=.bash_profile
suffix=
----- /Library/Application Support/
parentpath=/Library
fname=Application Support
fnameroot=Application Support
suffix=
----- readme.new.txt
parentpath=.
fname=readme.new.txt
fnameroot=readme.new
suffix=.txt
Saya berpikir bahwa jika anda hanya membutuhkan nama file, anda dapat mencoba ini:
FULLPATH=/usr/share/X11/xorg.conf.d/50-synaptics.conf
# Remove all the prefix until the "/" character
FILENAME=${FULLPATH##*/}
# Remove all the prefix until the "." character
FILEEXTENSION=${FILENAME##*.}
# Remove a suffix, in our case, the filename. This will return the name of the directory that contains this file.
BASEDIRECTORY=${FULLPATH%$FILENAME}
echo "path = $FULLPATH"
echo "file name = $FILENAME"
echo "file extension = $FILEEXTENSION"
echo "base directory = $BASEDIRECTORY"
Dan itu semua =D
Anda dapat memaksa dipotong untuk menampilkan semua kolom dan selanjutnya menambahkan -
untuk field nomor.
NAME=`basename "$FILE"`
EXTENSION=`echo "$NAME" | cut -d'.' -f2-`
Jadi jika FILE eth0.pcap.gz
, EKSTENSI akan pcap.gz
Menggunakan logika yang sama, anda juga dapat mengambil nama file menggunakan '-' dengan cut sebagai berikut :
NAME=`basename "$FILE" | cut -d'.' -f-1`
Ini bekerja bahkan untuk file yang tidak memiliki ekstensi.
Ok jadi jika saya mengerti benar, masalah di sini adalah cara untuk mendapatkan nama dan ekstensi file yang memiliki beberapa ekstensi, misalnya, stuff.tar.gz
.
Ini bekerja untuk saya:
fullfile="stuff.tar.gz"
fileExt=${fullfile#*.}
fileName=${fullfile%*.$fileExt}
Ini akan memberikan anda barang-barang
sebagai nama file dan .tar.gz
sebagai ekstensi. Ia bekerja untuk setiap nomor ekstensi, termasuk 0. Semoga ini bisa membantu bagi siapa saja yang memiliki masalah yang sama =)
Selain banyak jawaban yang baik pada Stack Overflow pertanyaan yang ingin saya tambahkan:
Di bawah Linux, dan lain unixen, ada ajaib perintah bernama file
, yang melakukan filetype deteksi dengan menganalisis beberapa byte pertama dari file. Ini adalah waktu yang sangat lama alat, semula digunakan untuk print server (jika tidak dibuat untuk... saya'm tidak yakin tentang hal itu).
file myfile.txt
myfile.txt: UTF-8 Unicode text
file -b --mime-type myfile.txt
text/plain
Standar ekstensi dapat ditemukan di /etc/mime.jenis
(saya Debian GNU/Linux desktop. Melihat pria file
dan man mime.jenis
. Mungkin anda harus menginstal file
utilitas dan mime-dukungan
paket):
grep $( file -b --mime-type myfile.txt ) </etc/mime.types
text/plain asc txt text pot brf srt
Anda bisa membuat [tag:bash] fungsi untuk menentukan ekstensi yang tepat. Ada sedikit (tidak sempurna) contoh:
file2ext() {
local _mimetype=$(file -Lb --mime-type "$1") _line _basemimetype
case ${_mimetype##*[/.-]} in
gzip | bzip2 | xz | z )
_mimetype=${_mimetype##*[/.-]}
_mimetype=${_mimetype//ip}
_basemimetype=$(file -zLb --mime-type "$1")
;;
stream )
_mimetype=($(file -Lb "$1"))
[ "${_mimetype[1]}" = "compressed" ] &&
_basemimetype=$(file -b --mime-type - < <(
${_mimetype,,} -d <"$1")) ||
_basemimetype=${_mimetype,,}
_mimetype=${_mimetype,,}
;;
executable ) _mimetype='' _basemimetype='' ;;
dosexec ) _mimetype='' _basemimetype='exe' ;;
shellscript ) _mimetype='' _basemimetype='sh' ;;
* )
_basemimetype=$_mimetype
_mimetype=''
;;
esac
while read -a _line ;do
if [ "$_line" == "$_basemimetype" ] ;then
[ "$_line[1]" ] &&
_basemimetype=${_line[1]} ||
_basemimetype=${_basemimetype##*[/.-]}
break
fi
done </etc/mime.types
case ${_basemimetype##*[/.-]} in
executable ) _basemimetype='' ;;
shellscript ) _basemimetype='sh' ;;
dosexec ) _basemimetype='exe' ;;
* ) ;;
esac
[ "$_mimetype" ] && [ "$_basemimetype" != "$_mimetype" ] &&
printf ${2+-v} $2 "%s.%s" ${_basemimetype##*[/.-]} ${_mimetype##*[/.-]} ||
printf ${2+-v} $2 "%s" ${_basemimetype##*[/.-]}
}
Fungsi ini dapat mengatur Bash variabel yang dapat digunakan di kemudian hari:
(Ini terinspirasi dari @Petesh jawaban yang benar):
filename=$(basename "$fullfile")
filename="${filename%.*}"
file2ext "$fullfile" extension
echo "$fullfile -> $filename . $extension"
$ F = "text file.test.txt"
$ echo ${F/*./}
txt
Ini melayani beberapa titik dan spasi pada nama file, namun jika tidak ada perpanjangan itu kembali nama file itu sendiri. Mudah untuk memeriksa meskipun, hanya tes untuk nama file dan ekstensi yang sama.
Tentu saja metode ini doesn't bekerja untuk .tar.gz file. Namun yang bisa ditangani dalam proses dua langkah. Jika ekstensi gz kemudian periksa lagi untuk melihat apakah ada juga ekstensi tar.
Cara mengekstrak nama file dan ekstensi di ikan:
function split-filename-extension --description "Prints the filename and extension"
for file in $argv
if test -f $file
set --local extension (echo $file | awk -F. '{print $NF}')
set --local filename (basename $file .$extension)
echo "$filename $extension"
else
echo "$file is not a valid file"
end
end
end
Peringatan: Perpecahan pada titik terakhir, yang bekerja dengan baik untuk nama file dengan titik-titik di dalamnya, tapi tidak baik untuk ekstensi dengan titik-titik di dalamnya. Lihat contoh di bawah ini.
Penggunaan:
$ split-filename-extension foo-0.4.2.zip bar.tar.gz
foo-0.4.2 zip # Looks good!
bar.tar gz # Careful, you probably want .tar.gz as the extension.
Ada's mungkin cara yang lebih baik untuk melakukan hal ini. Merasa bebas untuk mengedit jawaban saya untuk memperbaikinya.
Jika ada's terbatas set ekstensi anda'akan berurusan dengan dan anda tahu mereka semua, coba ini:
switch $file
case *.tar
echo (basename $file .tar) tar
case *.tar.bz2
echo (basename $file .tar.bz2) tar.bz2
case *.tar.gz
echo (basename $file .tar.gz) tar.gz
# and so on
end
Ini tidak tidak memiliki peringatan sebagai contoh pertama, tetapi anda harus menangani setiap kasus sehingga bisa menjadi lebih membosankan tergantung pada berapa banyak ekstensi yang dapat anda harapkan.