Extraire le nom de fichier et l'extension à Bash
je veux obtenir le nom de fichier (sans extension) et l'extension séparément.
la meilleure solution que j'ai trouvée jusqu'à présent est:
NAME=`echo "$FILE" | cut -d'.' -f1`
EXTENSION=`echo "$FILE" | cut -d'.' -f2`
ceci est erroné parce que cela ne fonctionne pas si le nom du fichier contient plusieurs caractères .
. Si, disons, j'ai a.b.js
, il considérera a
et b.js
, au lieu de a.b
et js
.
Il peut être facilement fait en Python avec
file, ext = os.path.splitext(path)
mais je préférerais ne pas allumer un interpréteur Python juste pour ça, si possible.
de meilleures idées?
30 réponses
tout d'abord, obtenir le nom du fichier sans le chemin:
filename=$(basename -- "$fullfile")
extension="${filename##*.}"
filename="${filename%.*}"
alternativement, vous pouvez vous concentrer sur le dernier '/' du chemin au lieu du '.'qui devrait fonctionner même si vous avez des extensions de fichier imprévisibles:
filename="${fullfile##*/}"
~% FILE="example.tar.gz"
~% echo "${FILE%%.*}"
example
~% echo "${FILE%.*}"
example.tar
~% echo "${FILE#*.}"
tar.gz
~% echo "${FILE##*.}"
gz
Pour plus de détails, voir shell paramètre d'extension dans le manuel de Bash.
habituellement, vous connaissez déjà l'extension, donc vous pourriez vouloir utiliser:
basename filename .extension
par exemple:
basename /path/to/dir/filename.txt .txt
et nous obtenons
filename
vous pouvez utiliser la magie des variables POSIX:
bash-3.2$ FILENAME=somefile.tar.gz
bash-3.2$ echo ${FILENAME%%.*}
somefile
bash-3.2$ echo ${FILENAME%.*}
somefile.tar
il y a une mise en garde dans le fait que si votre nom de fichier était de la forme ./somefile.tar.gz
alors echo ${FILENAME%%.*}
enlèverait greedily la plus longue correspondance au .
et vous auriez la chaîne vide.
( vous pouvez contourner cela avec une variable temporaire:
FULL_FILENAME=$FILENAME
FILENAME=${FULL_FILENAME##*/}
echo ${FILENAME%%.*}
)
Ce site explique plus.
${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
cela ne semble pas fonctionner si le fichier n'a pas d'extension, ou pas de nom de fichier. Voici ce que j'utilise; il n'utilise que des builtins et gère plus (mais pas tous) de noms de fichiers pathologiques.
#!/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
Et voici quelques testcases:
$ basename-and-extension.sh / /home/me/ /home/me/file /home/me/file.tar /home/me/file.tar.gz /home/me/.hidden /home/me/.hidden.tar /home/me/.. . /: dir = "/" base = "" ext = "" /home/me/: dir = "/home/me/" base = "" ext = "" /home/me/file: dir = "/home/me/" base = "file" ext = "" /home/me/file.tar: dir = "/home/me/" base = "file" ext = "tar" /home/me/file.tar.gz: dir = "/home/me/" base = "file.tar" ext = "gz" /home/me/.hidden: dir = "/home/me/" base = ".hidden" ext = "" /home/me/.hidden.tar: dir = "/home/me/" base = ".hidden" ext = "tar" /home/me/..: dir = "/home/me/" base = ".." ext = "" .: dir = "" base = "." ext = ""
vous pouvez utiliser basename
.
exemple:
$ basename foo-bar.tar.gz .tar.gz
foo-bar
Vous devez fournir les basename avec l'extension qui doit être supprimé, cependant, si vous êtes toujours d'exécution tar
avec -z
alors, vous savez, l'extension sera .tar.gz
.
Ce qui devrait faire ce que vous voulez:
tar -zxvf
cd $(basename .tar.gz)
pax> echo a.b.js | sed 's/\.[^.]*$//'
a.b
pax> echo a.b.js | sed 's/^.*\.//'
js
fonctionne très bien, donc vous pouvez juste utiliser:
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
les commandes, soit dit en passant, fonctionnent comme suit.
la commande pour NAME
remplace un caractère "."
suivi de n'importe quel nombre de caractères non - "."
jusqu'à la fin de la ligne, sans rien (i.e., il enlève tout du final "."
jusqu'à la fin de la ligne, inclus). Il s'agit essentiellement d'une substitution non cupide utilisant la ruse regex.
la commande pour EXTENSION
remplace un certain nombre de caractères suivi d'un "."
caractère au début de la ligne, sans rien (c.-à-d., Il supprime tout du début de la ligne au point final, inclus). C'est une substitution cupide qui est l'action par défaut.
Mellen écrit dans un commentaire sur un billet de blog:
en utilisant Bash, il y a aussi ${file%.*}
pour obtenir le nom du fichier sans l'extension et ${file##*.}
pour obtenir l'extension seul. C'est-à-dire,
file="thisfile.txt"
echo "filename: ${file%.*}"
echo "extension: ${file##*.}"
sorties:
filename: thisfile
extension: txt
vous pouvez utiliser la commande cut
pour supprimer les deux dernières extensions (la partie ".tar.gz"
):
$ echo "foo.tar.gz" | cut -d'.' --complement -f2-
foo
comme L'a souligné Clayton Hughes dans un commentaire, cela ne fonctionnera pas pour l'exemple réel dans la question. Ainsi, comme alternative, je propose d'utiliser sed
avec des expressions régulières étendues, comme ceci:
$ echo "mpc-1.0.1.tar.gz" | sed -r 's/\.[[:alnum:]]+\.[[:alnum:]]+$//'
mpc-1.0.1
il fonctionne en supprimant les deux derniers (alpha-numérique) extensions sans condition.
[mise à jour après le commentaire D'Anders Lindahl]
Pas besoin de s'embêter avec awk
ou sed
ou même perl
pour cette simple tâche. Il existe une solution os.path.splitext()
compatible pur-Bash qui n'utilise que des extensions de paramètres.
Mise En Œuvre De Référence
Documentation de os.path.splitext(path)
:
divise le chemin de chemin en une paire
(root, ext)
telle queroot + ext == path
, et ext est vide ou commence par une période et contient au plus une période. Les périodes de référence sur le basename sont ignorées;splitext('.cshrc')
retourne('.cshrc', '')
.
code Python:
root, ext = os.path.splitext(path)
Bash Mise En Œuvre
Honorer le leader des périodes de
root="${path%.*}"
ext="${path#"$root"}"
en Ignorant les périodes de pointe
root="${path#.}";root="${path%"$root"}${root%.*}"
ext="${path#"$root"}"
Essais
Voici cas d'essai pour l'implémentation Ignoring leading periods , qui devrait correspondre à L'implémentation de référence Python pour chaque entrée.
|---------------|-----------|-------|
|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' |
|---------------|-----------|-------|
Résultats Des Essais
tous les essais sont réussis.
voici quelques suggestions alternatives (la plupart dans awk
), y compris certains cas d'utilisation avancée, comme l'extraction des numéros de version pour les paquets de logiciels.
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(/[^.]*[.]/, "", "151900920")} 1'
# Extension (last-2): 'tar.gz'
echo "$f" | awk -F'[.]' '{print $(NF-1)"."$NF}'
# Basename : 'file'
echo "$f" | awk '{gsub(/.*[/]|[.].*/, "", "151900920")} 1'
# Basename-extended : 'file.1.0.1.tar'
echo "$f" | awk '{gsub(/.*[/]|[.]{1}[^.]+$/, "", "151900920")} 1'
# Path : '/path/to/complex/'
echo "$f" | awk '{match("151900920", /.*[/]/, a); print a[0]}'
# or
echo "$f" | grep -Eo '.*[/]'
# Folder (containing the file) : 'complex'
echo "$f" | awk -F'/' '{=""; 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'[/.]' '{=""; print "151900920"}'
# Is absolute : True (exit-code : 0)
# Return true if it is an absolute path (starting with '/' or '~/'
echo "$f" | grep -q '^[/]\|^~/'
tous les cas d'utilisation utilisent le chemin complet original comme entrée, sans dépendre des résultats intermédiaires.
[révisé d'une doublure unique à une bash Générique fonction , comportement maintenant compatible avec dirname
et basename
utilitaires; justification ajoutée.]
Le accepté de répondre fonctionne bien typique cas , mais échoue dans edge cas , à savoir:
- pour les noms de fichiers sans l'extension (appelé suffixe dans le reste de cette réponse),
extension=${filename##*.}
renvoie le fichier d'entrée plutôt qu'une chaîne vide. -
extension=${filename##*.}
ne comprend pas le.
initial , contrairement à la convention.- préparation Aveugle
.
ne fonctionnerait pas pour les noms de fichiers sans suffixe.
- préparation Aveugle
-
filename="${filename%.*}"
sera une chaîne vide si le fichier d'entrée nom commence par.
et ne contient pas d'autres.
caractères (par exemple,.bash_profile
) - contrairement à la convention.
---------
ainsi, la complexité d'une solution robuste qui couvre toutes les arêtes cas appelle à une fonction - voir sa définition ci-dessous; il peut retourner tout composants d'un chemin .
exemple d'appel:
splitPath '/etc/bash.bashrc' dir fname fnameroot suffix
# -> $dir == '/etc'
# -> $fname == 'bash.bashrc'
# -> $fnameroot == 'bash'
# -> $suffix == '.bashrc'
notez que les arguments après le chemin d'entrée sont librement choisis, variable de position noms .
Pour sauter les variables qui ne sont pas d'intérêt et qui précèdent celles qui le sont, spécifiez _
(pour utiliser la variable de rejet $_
) ou ''
; par exemple, pour extraire la racine du nom de fichier et l'extension seulement, utilisez splitPath '/etc/bash.bashrc' _ _ fnameroot extension
.
# 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 "")
_sp_basename=$(basename "")
# 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 ]] && printf -v "" "$_sp_dirname"
[[ -n ]] && printf -v "" "$_sp_basename"
[[ -n ]] && printf -v "" "$_sp_basename_root"
[[ -n ]] && printf -v "" "$_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
code D'essai qui exerce la fonction:
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
résultats escomptés-noter les cas de bord:
- un nom de fichier ayant pas de suffixe
- un nom de fichier commençant par
.
( pas considéré comme le début du suffixe) - un chemin d'entrée se terminant par
/
("derrière/
est ignoré) - un chemin d'entrée qui est un nom de fichier (
.
est retourné en tant que parent chemin) - un nom de fichier qui a plus de
.
précédé d'un jeton (seul le dernier est considéré comme le suffixe):
----- /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
je pense que si vous avez juste besoin du nom du fichier, vous pouvez essayer ceci:
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"
et c'est tout =D.
la solution la plus petite et la plus simple (en une seule ligne) est:
$ file=/blaabla/bla/blah/foo.txt
echo $(basename ${file%.*})
foo
vous pouvez forcer cut à afficher tous les champs et les suivants en ajoutant -
au numéro de champ.
NAME=`basename "$FILE"`
EXTENSION=`echo "$NAME" | cut -d'.' -f2-`
donc si le fichier est eth0.pcap.gz
, L'EXTENSION sera pcap.gz
en utilisant la même logique, vous pouvez également récupérer le nom du fichier en utilisant '-' avec cut comme suit:
NAME=`basename "$FILE" | cut -d'.' -f-1`
Cela fonctionne même pour les noms de fichiers qui n'ont pas d'extension.
Ok donc si je comprends bien, le problème ici est de savoir comment obtenir le nom et l'extension complète d'un fichier qui a plusieurs extensions, par exemple, stuff.tar.gz
.
ça marche pour moi:
fullfile="stuff.tar.gz"
fileExt=${fullfile#*.}
fileName=${fullfile%*.$fileExt}
cela vous donnera stuff
comme nom de fichier et .tar.gz
comme extension. Il fonctionne pour n'importe quel nombre d'extensions, y compris 0. Espérons que cela aide pour toute personne ayant le même problème =)
la Magie de reconnaissance de fichiers
en plus des nombreuses bonnes réponses sur cette question de débordement de pile, j'aimerais ajouter:
sous Linux et autres unixen , il y a une magic commande nommée file
, qui font la détection de type de fichier en analysant certains premiers octets du fichier. C'est un très vieil outil, initialement utilisé pour les serveurs d'impression (si ce n'est créée... Je ne suis pas sûr à ce sujet).
file myfile.txt
myfile.txt: UTF-8 Unicode text
file -b --mime-type myfile.txt
text/plain
les extensions Standards peuvent être trouvées dans /etc/mime.types
(sur mon Debian GNU/Linux desktop. Voir man file
et man mime.types
. Peut-être que vous devez installer l'utilitaire file
et les paquets mime-support
):
grep $( file -b --mime-type myfile.txt ) </etc/mime.types
text/plain asc txt text pot brf srt
vous pouvez créer une fonction bash pour déterminer l'extension droite. Il y a un petit (pas parfait) échantillon:
file2ext() {
local _mimetype=$(file -Lb --mime-type "") _line _basemimetype
case ${_mimetype##*[/.-]} in
gzip | bzip2 | xz | z )
_mimetype=${_mimetype##*[/.-]}
_mimetype=${_mimetype//ip}
_basemimetype=$(file -zLb --mime-type "")
;;
stream )
_mimetype=($(file -Lb ""))
[ "${_mimetype[1]}" = "compressed" ] &&
_basemimetype=$(file -b --mime-type - < <(
${_mimetype,,} -d <"")) ||
_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} "%s.%s" ${_basemimetype##*[/.-]} ${_mimetype##*[/.-]} ||
printf ${2+-v} "%s" ${_basemimetype##*[/.-]}
}
cette fonction pourrait définir Variable Bash qui peut être utilisée plus tard:
(c'est inspiré de la réponse à droite de @Petesh):
filename=$(basename "$fullfile")
filename="${filename%.*}"
file2ext "$fullfile" extension
echo "$fullfile -> $filename . $extension"
$ F = "text file.test.txt"
$ echo ${F/*./}
txt
cela s'applique à plusieurs points et Espaces Dans un nom de fichier, mais s'il n'y a pas d'extension, il renvoie le nom du fichier lui-même. Facile à vérifier cependant; juste tester pour le nom de fichier et l'extension étant la même.
naturellement, cette méthode ne fonctionne pas .tar.les fichiers gz. Toutefois, qui pourraient être traitées dans un processus en deux étapes. Si l'extension est gz, vérifiez à nouveau s'il y a aussi une extension tar.
j'utilise le script suivant
$ echo "foo.tar.gz"|rev|cut -d"." -f3-|rev
foo
Comment extraire le nom de fichier et l'extension en poisson :
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
mises en garde: fentes sur le dernier point, qui fonctionne bien pour les noms de fichiers avec des points en eux, mais pas bien pour les extensions avec des points en eux. Voir l'exemple ci-dessous.
Utilisation:
$ 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.
Il y a probablement de meilleures façons de le faire. N'hésitez pas à modifier mon réponse de l'améliorer.
S'il y a un nombre limité d'extensions avec lesquelles vous aurez affaire et que vous connaissez toutes, essayez ceci:
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
Ce n' pas ont de la mise en garde que le premier exemple, mais vous n'avez pas à gérer tous les cas de sorte qu'il pourrait être plus ennuyeux en fonction du nombre d'extensions que vous pouvez vous attendre.
voici le code avec AWK . Cela peut être fait plus simplement. Mais je ne suis pas bon à AWK.
filename$ ls
abc.a.txt a.b.c.txt pp-kk.txt
filename$ find . -type f | awk -F/ '{print }' | rev | awk -F"." '{="";print}' | rev | awk 'gsub(" ",".") ,sub(".$", "")'
abc.a
a.b.c
pp-kk
filename$ find . -type f | awk -F/ '{print }' | awk -F"." '{print $NF}'
txt
txt
txt
une réponse simple:
pour développer sur le variables POSIX réponse , notez que vous pouvez faire des modèles plus intéressants. Donc pour le cas détaillé ici, vous pouvez simplement faire ceci:
tar -zxvf
cd ${1%.tar.*}
qui coupera la dernière occurrence de .tar.
plus généralement, si vous voulez supprimer la dernière occurrence de . .
${1.*.*}
devrait marcher.
Le lien de la réponse ci-dessus semble être mort. voici une excellente explication d'un tas de manipulations de cordes que vous pouvez faire directement dans Bash, à partir de TLDP .
utiliser simplement ${parameter%word}
dans votre cas:
${FILE%.*}
si vous voulez le tester, tous les travaux suivants, et il suffit de supprimer l'extension:
FILE=abc.xyz; echo ${FILE%.*};
FILE=123.abc.xyz; echo ${FILE%.*};
FILE=abc; echo ${FILE%.*};
basé en grande partie sur l'excellente réponse de @mklement0, et la pile-pleine de hasard, utile bashismes - ainsi que d'autres réponses à cette / d'autres questions / "que diable internet"... Je l'ai enveloppé dans un peu, un peu plus compréhensible, réutilisable fonction pour mon (ou votre) .bash_profile
qui prend soin de ce qui (je considère) devrait être une version plus robuste de dirname
/ basename
/ qu'avez-vous ..
function path { SAVEIFS=$IFS; IFS="" # stash IFS for safe-keeping, etc.
[[ $# != 2 ]] && echo "usage: path <path> <dir|name|fullname|ext>" && return # demand 2 arguments
[[ =~ ^(.*/)?(.+)?$ ]] && { # regex parse the path
dir=${BASH_REMATCH[1]}
file=${BASH_REMATCH[2]}
ext=$([[ $file = *.* ]] && printf %s ${file##*.} || printf '')
# edge cases for extesionless files and files like ".nesh_profile.coffee"
[[ $file == $ext ]] && fnr=$file && ext='' || fnr=${file:0:$((${#file}-${#ext}))}
case "" in
dir) echo "${dir%/*}"; ;;
name) echo "${fnr%.*}"; ;;
fullname) echo "${fnr%.*}.$ext"; ;;
ext) echo "$ext"; ;;
esac
}
IFS=$SAVEIFS
}
exemples d'utilisation...
SOMEPATH=/path/to.some/.random\ file.gzip
path $SOMEPATH dir # /path/to.some
path $SOMEPATH name # .random file
path $SOMEPATH ext # gzip
path $SOMEPATH fullname # .random file.gzip
path gobbledygook # usage: -bash <path> <dir|name|fullname|ext>
des réponses ci-dessus, le plus court oneliner pour imiter Python
file, ext = os.path.splitext(path)
présumant que votre fichier a réellement une extension, est
EXT="${PATH##*.}"; FILE=$(basename "$PATH" .$EXT)
si vous voulez aussi autoriser empty extensions, c'est la plus courte que je puisse trouver:
echo 'hello.txt' | sed -r 's/.+\.(.+)|.*//' # EXTENSION
echo 'hello.txt' | sed -r 's/(.+)\..+|(.*)//' # FILENAME
1ère ligne expliquée: elle correspond au chemin.EXT or ANYTHING and replaces it with EXT. S'il y a correspondance, le groupe ext n'est pas capturé.
afin de rendre dir plus utile (dans le cas où un fichier local sans chemin est spécifié comme entrée) j'ai fait ce qui suit:
# Substring from 0 thru pos of filename
dir="${fullpath:0:${#fullpath} - ${#filename}}"
if [[ -z "$dir" ]]; then
dir="./"
fi
Cela permet de faire quelque chose d'utile comme ajouter un suffixe au fichier d'entrée basename:
outfile=${dir}${base}_suffix.${ext}
testcase: foo.bar
dir: "./"
base: "foo"
ext: "bar"
outfile: "./foo_suffix.bar"
testcase: /home/me/foo.bar
dir: "/home/me/"
base: "foo"
ext: "bar"
outfile: "/home/me/foo_suffix.bar"
vous pouvez utiliser
sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
pour obtenir le nom de fichier et
sed 's/^/./' | rev | cut -d. -f1 | rev
pour obtenir une extension.
cas de Test:
echo "filename.gz" | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename.gz" | sed 's/^/./' | rev | cut -d. -f1 | rev
echo "filename" | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename" | sed 's/^/./' | rev | cut -d. -f1 | rev
echo "filename.tar.gz" | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename.tar.gz" | sed 's/^/./' | rev | cut -d. -f1 | rev
Voici l'algorithme que j'ai utilisé pour trouver le nom et l'extension d'un fichier lorsque j'ai écrit un script Bash pour rendre les noms uniques lorsque les noms entraient en conflit par rapport au casing.
#! /bin/bash
#
# Finds
# -- name and extension pairs
# -- null extension when there isn't an extension.
# -- Finds name of a hidden file without an extension
#
declare -a fileNames=(
'.Montreal'
'.Rome.txt'
'Loundon.txt'
'Paris'
'San Diego.txt'
'San Francisco'
)
echo "Script "151900920" finding name and extension pairs."
echo
for theFileName in "${fileNames[@]}"
do
echo "theFileName=${theFileName}"
# Get the proposed name by chopping off the extension
name="${theFileName%.*}"
# get extension. Set to null when there isn't an extension
# Thanks to mklement0 in a comment above.
extension=$([[ "$theFileName" == *.* ]] && echo ".${theFileName##*.}" || echo '')
# a hidden file without extenson?
if [ "${theFileName}" = "${extension}" ] ; then
# hidden file without extension. Fixup.
name=${theFileName}
extension=""
fi
echo " name=${name}"
echo " extension=${extension}"
done
l'essai.
$ config/Name\&Extension.bash
Script config/Name&Extension.bash finding name and extension pairs.
theFileName=.Montreal
name=.Montreal
extension=
theFileName=.Rome.txt
name=.Rome
extension=.txt
theFileName=Loundon.txt
name=Loundon
extension=.txt
theFileName=Paris
name=Paris
extension=
theFileName=San Diego.txt
name=San Diego
extension=.txt
theFileName=San Francisco
name=San Francisco
extension=
$
FYI: le programme complet de translittération et plus de cas d'essai peuvent être trouvés ici: https://www.dropbox.com/s/4c6m0f2e28a1vxf/avoid-clashes-code.zip?dl=0