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`
Cela est faux car cela ne fonctionne pas si le nom du fichier contient plusieurs caractères .
. Si, par exemple, j'ai a.b.js
, il considérera a
et b.js
au lieu de a.b
et js
.
Cela peut être facilement fait en Python avec
file, ext = os.path.splitext(path)
mais je préférerais ne pas lancer un interprète Python uniquement pour cela, si possible.
De meilleures idées?
Commencez par 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 '.' ce qui devrait fonctionner même si vous avez des extensions de fichier imprévisibles:
filename="${fullfile##*/}"
Vous voudrez peut-être vérifier la documentation:
~% FILE="example.tar.gz"
~% echo "${FILE%%.*}"
example
~% echo "${FILE%.*}"
example.tar
~% echo "${FILE#*.}"
tar.gz
~% echo "${FILE##*.}"
gz
Pour plus de détails, voir expansion des paramètres du shell dans le manuel Bash.
Habituellement, vous connaissez déjà l'extension, vous pouvez donc 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%%.*}
effacerait goulûment la correspondance la plus longue avec le .
et vous auriez la chaîne vide.
(Vous pouvez contourner ce problème 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 de nom de fichier. Voici ce que j'utilise. il utilise uniquement les commandes intégrées et gère plus de noms de fichiers pathologiques (mais pas tous).
#!/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 cas de test:
$ basename-and-extension.sh// home/me// home/me/fichier /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 =" fichier " ext =" " /home/me/file.tar: dir = "/home/me/" base =" fichier " ext =" tar " /home/me/file. tar.gz: dir = "/home/me/" base =" fichier.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 le nom de base avec l'extension à supprimer. Toutefois, si vous exécutez toujours tar
avec -z
, vous savez que l'extension sera .tar.gz
.
Cela devrait faire ce que vous voulez:
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
fonctionne bien, vous pouvez donc simplement 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, en passant, fonctionnent comme suit.
La commande pour NAME
remplace un caractère "."
suivi d'un nombre quelconque de caractères non -"."
jusqu'à la fin de la ligne, sans rien (c'est-à-dire qu'il supprime tout du dernier "."
jusqu'à la fin de la ligne, inclus). Il s’agit essentiellement d’une substitution non gourmande utilisant la ruse regex.
La commande pour EXTENSION
remplace un nombre quelconque de caractères suivis par un caractère "."
au début de la ligne, sans rien (c'est-à-dire qu'elle supprime tout du début à la dernière ligne, y compris ). C'est une substitution gloutonne qui est l'action par défaut.
Mellen écrit dans un commentaire sur un article de blog:
En utilisant Bash, il y a aussi ${file%.*}
pour obtenir le nom du fichier sans extension et ${file##*.}
pour obtenir l’extension seule. C'est,
file="thisfile.txt"
echo "filename: ${file%.*}"
echo "extension: ${file##*.}"
Les 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 Clayton Hughes l'a noté dans un commentaire, cela ne fonctionnera pas pour l'exemple présenté dans la question. Donc, 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
Cela fonctionne en supprimant les deux dernières extensions (alphanumériques) sans condition.
[Mis à jour à nouveau après le commentaire de Anders Lindahl]
Pas besoin de s'embêter avec awk
ou sed
ou même Perl
pour cette tâche simple. Il existe une solution compatible pure-Bash, os.path.splitext()
, qui utilise uniquement des extensions de paramètres.
Documentation de os.path.splitext(path)
:
Scindez le chemin du chemin d'accès en une paire
(root, ext)
de telle sorte queroot + ext == path
, et ext est vide ou commence par un point et contient la plupart une période. Les périodes principales du nom de base sont ignorées;splitext('.cshrc')
renvoie('.cshrc', '')
.
Code Python:
root, ext = os.path.splitext(path)
root="${path%.*}"
ext="${path#"$root"}"
root="${path#.}";root="${path%"$root"}${root%.*}"
ext="${path#"$root"}"
Voici des cas de test pour l'implémentation en ignorant les périodes principales , qui doit correspondre à l'implémentation de référence Python sur 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' |
|---------------|-----------|-------|
Tous les tests ont réussi.
Voici quelques suggestions alternatives (principalement dans awk
), y compris des cas d'utilisation avancés, tels que l'extraction des numéros de version pour les packages 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(/[^.]*[.]/, "", $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 '^[/]\|^~/'
Tous les cas d'utilisation utilisent le chemin complet d'origine en entrée, sans dépendre de résultats intermédiaires.
La solution la plus simple et la plus simple (sur une seule ligne) est la suivante:
$ file=/blaabla/bla/blah/foo.txt
echo $(basename ${file%.*}) # foo
Le réponse acceptée fonctionne bien dans cas typiques , mais échoue dans Edge cas, à savoir:
extension=${filename##*.}
renvoie le nom du fichier en entrée plutôt qu'une chaîne vide.extension=${filename##*.}
n'inclut pas le .
initial, contrairement à la convention. .
ne fonctionne pas pour les noms de fichiers sans suffixe.filename="${filename%.*}"
sera la chaîne vide, si le nom du fichier en entrée commence par .
et ne contient plus aucun caractère .
(par exemple, .bash_profile
), contrairement à la convention.Ainsi, la complexité d'une solution robuste couvrant tous les cas Edge appelle une fonction - voir sa définition ci-dessous; il peut retourner tous les 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 choisis librement, variable de position , noms .
Pour ignorer les variables sans intérêt précédant celles qui le sont, spécifiez _
(pour utiliser la variable jetable $_
) ou ''
; Par exemple, pour extraire le nom de fichier racine et son extension uniquement, 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 "$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
Code de test 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 attendus - notez les cas Edge:
.
( et non considéré comme le début du suffixe)/
(le /
suivant est ignoré).
est renvoyé en tant que chemin parent).
- jeton préfixé (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.
Vous pouvez forcer la coupe pour 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 FILE est eth0.pcap.gz
, EXTENSION sera pcap.gz
En utilisant la même logique, vous pouvez également extraire le nom du fichier en utilisant '-' avec couper comme suit:
NAME=`basename "$FILE" | cut -d'.' -f-1`
Cela fonctionne même pour les noms de fichiers qui n'ont aucune extension.
Ok, donc si je comprends bien, le problème est de savoir comment obtenir le nom et l’extension complète d’un fichier comportant plusieurs extensions, par exemple stuff.tar.gz
.
Cela fonctionne pour moi:
fullfile="stuff.tar.gz"
fileExt=${fullfile#*.}
fileName=${fullfile%*.$fileExt}
Cela vous donnera stuff
comme nom de fichier et .tar.gz
comme extension. Cela fonctionne pour n'importe quel nombre d'extensions, y compris 0. J'espère que cela aidera pour tous ceux qui ont le même problème =)
En plus des nombreuses bonnes réponses à cette question de débordement de pile, j'aimerais ajouter:
Sous Linux et d'autres Unix, il existe une commande magique nommée file
qui détecte le type de fichier en analysant certains premiers octets du fichier. C'est un très vieil outil, initialement utilisé pour les serveurs d'impression (s'il n'a pas été créé pour ... je n'en suis pas sûr).
file myfile.txt
myfile.txt: UTF-8 Unicode text
file -b --mime-type myfile.txt
text/plain
Des extensions de standards peuvent être trouvées dans /etc/mime.types
(sur mon Debian Bureau GNU/Linux. Voir man file
et man mime.types
. Peut-être devez-vous installer le file
Utility et mime-support
packages):
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 la bonne extension. Il y a un petit échantillon (pas parfait):
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##*[/.-]}
}
Cette fonction pourrait définir une variable Bash utilisable ultérieurement:
(Ceci est inspiré de la bonne réponse de @Petesh):
filename=$(basename "$fullfile")
filename="${filename%.*}"
file2ext "$fullfile" extension
echo "$fullfile -> $filename . $extension"
J'utilise le script suivant
$ echo "foo.tar.gz"|rev|cut -d"." -f3-|rev
foo
$ F = "text file.test.txt"
$ echo ${F/*./}
txt
Ceci concerne plusieurs points et espaces dans un nom de fichier. Toutefois, s'il n'y a pas d'extension, il retourne le nom de fichier lui-même. Facile à vérifier cependant; il suffit de tester si le nom de fichier et l'extension sont identiques.
Naturellement, cette méthode ne fonctionne pas pour les fichiers .tar.gz. Cependant, cela pourrait être traité en deux étapes. Si l'extension est gz, vérifiez à nouveau s'il existe également une extension tar.
Comment extraire le nom de fichier et l'extension en fish :
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: Divise le dernier point, ce qui fonctionne bien pour les noms de fichiers contenant des points, mais pas pour les extensions comportant des points. Voir 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 à éditer ma réponse pour l'améliorer.
Si vous avez affaire à un nombre limité d'extensions et que vous les 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
Ceci ( ne contient pas comme premier exemple, mais vous devez gérer tous les cas, ce qui pourrait être plus fastidieux en fonction du nombre d'extensions que vous pouvez attendre.
Voici le code avec AWK . Cela peut être fait plus simplement. Mais je ne suis pas bon dans AWK.
filename$ ls
abc.a.txt a.b.c.txt pp-kk.txt
filename$ find . -type f | awk -F/ '{print $2}' | rev | awk -F"." '{$1="";print}' | rev | awk 'gsub(" ",".") ,sub(".$", "")'
abc.a
a.b.c
pp-kk
filename$ find . -type f | awk -F/ '{print $2}' | awk -F"." '{print $NF}'
txt
txt
txt
Construire à partir de Petesh , si seul le nom de fichier est requis, les chemins et les extensions peuvent être supprimés sur une seule ligne,
filename=$(basename ${fullname%.*})
Utilisez simplement ${parameter%Word}
Dans ton cas:
${FILE%.*}
Si vous voulez le tester, tout le travail suivant, et simplement supprimer l'extension:
FILE=abc.xyz; echo ${FILE%.*};
FILE=123.abc.xyz; echo ${FILE%.*};
FILE=abc; echo ${FILE%.*};
Basé en grande partie sur l'excellent réseau de @ mklement0, et regorgeant de hasard, utile bashismes - ainsi que d'autres réponses à cette/d'autres questions/"ce foutu Internet" ... J'ai tout emballé un peu, un peu plus compréhensible, réutilisable fonction pour mon (ou votre) .bash_profile
qui prend soin de ce que (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
[[ $1 =~ ^(.*/)?(.+)?$ ]] && { # regex parse the path
dir=${BASH_REMATCH[1]}
file=${BASH_REMATCH[2]}
ext=$([[ $file = *.* ]] && printf %s ${file##*.} || printf '')
# Edge cases for extensionless files and files like ".nesh_profile.coffee"
[[ $file == $ext ]] && fnr=$file && ext='' || fnr=${file:0:$((${#file}-${#ext}))}
case "$2" 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>
Si vous souhaitez également autoriser les extensions vide, voici la plus courte des réponses:
echo 'hello.txt' | sed -r 's/.+\.(.+)|.*/\1/' # EXTENSION
echo 'hello.txt' | sed -r 's/(.+)\..+|(.*)/\1\2/' # FILENAME
1ère ligne expliquée: Il correspond à PATH.EXT ou à ANYTHING et le remplace par EXT. Si ANYTHING était apparié, le groupe ext n'est pas capturé.
Une réponse simple:
Pour développer la variable POSIX réponse , notez que vous pouvez créer des motifs plus intéressants. Donc, pour le cas détaillé ici, vous pouvez simplement faire ceci:
tar -zxvf $1
cd ${1%.tar.*}
Cela coupera la dernière occurrence de .tar. <quelque chose> .
Plus généralement, si vous souhaitez supprimer la dernière occurrence de. <quelque chose> . <quelque chose-d'autre> alors
${1.*.*}
devrait bien fonctionner.
Le lien la réponse ci-dessus semble être mort. Voici une excellente explication de la manipulation de chaîne que vous pouvez faire directement dans Bash, à partir de TLDP .
C'est le seul qui a fonctionné pour moi:
path='folder/other_folder/file.js'
base=${path##*/}
echo ${base%.*}
>> file
Cela peut également être utilisé en interpolation de chaîne, mais vous devez malheureusement définir au préalable base
.
Parmi les réponses ci-dessus, le plus court oneliner à imiter Python
file, ext = os.path.splitext(path)
présumer que votre fichier a vraiment une extension, est
EXT="${PATH##*.}"; FILE=$(basename "$PATH" .$EXT)
IMHO la meilleure solution a déjà été donnée (en utilisant l'expansion des paramètres de Shell) et sont les mieux notés pour le moment.
J'ajoute cependant celui-ci qui utilise simplement des commandes dumbs, ce qui n'est pas efficace et que personne de grave ne devrait utiliser jamais:
FILENAME=$(echo $FILE | cut -d . -f 1-$(printf $FILE | tr . '\n' | wc -l))
EXTENSION=$(echo $FILE | tr . '\n' | tail -1)
Ajouté juste pour le plaisir :-)
En utilisant le fichier d'exemple /Users/Jonathan/Scripts/bash/MyScript.sh
, ce code:
MY_EXT=".${0##*.}"
ME=$(/usr/bin/basename "${0}" "${MY_EXT}")
${ME}
sera MyScript
et ${MY_EXT}
sera .sh
:
Scénario:
#!/bin/bash
set -e
MY_EXT=".${0##*.}"
ME=$(/usr/bin/basename "${0}" "${MY_EXT}")
echo "${ME} - ${MY_EXT}"
Quelques tests:
$ ./MyScript.sh
MyScript - .sh
$ bash MyScript.sh
MyScript - .sh
$ /Users/Jonathan/Scripts/bash/MyScript.sh
MyScript - .sh
$ bash /Users/Jonathan/Scripts/bash/MyScript.sh
MyScript - .sh
Peut-être qu'il y a une option dans tar
pour le faire; avez-vous vérifié l'homme? Sinon, vous pouvez utiliser extension de chaîne Bash :
test="mpc-1.0.1.tar.gz"
noExt="${test/.tar.gz/}" # Remove the string '.tar.gz'
echo $noExt
Afin de rendre dir plus utile (dans le cas où un fichier local sans chemin est spécifié en 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 vous permet de faire quelque chose d’utile comme ajouter un suffixe au nom du fichier d’entrée en tant que:
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"
Voici l'algorithme que j'ai utilisé pour trouver le nom et l'extension d'un fichier lorsque j'ai écrit un script Bash afin de rendre les noms uniques lorsque les noms sont en conflit par rapport à la casse.
#! /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 ${0} 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
Le test effectué.
$ 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 d’autres tests sont disponibles ici: https://www.dropbox.com/s/4c6m0f2e28a1vxf/avoid-clashes-code.zip?dl=
Vous pouvez utiliser
sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
pour obtenir le nom du 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 une solution sed
qui extrait les composants de chemin sous différentes formes et peut gérer la plupart des cas Edge:
_## Enter the input path and field separator character, for example:
## (separatorChar must not be present in inputPath)
inputPath="/path/to/Foo.bar"
separatorChar=":"
## sed extracts the path components and assigns them to output variables
oldIFS="$IFS"
IFS="$separatorChar"
read dirPathWithSlash dirPath fileNameWithExt fileName fileExtWithDot fileExt <<<"$(sed -En '
s/^[[:space:]]+//
s/[[:space:]]+$//
t l1
:l1
s/^([^/]|$)//
t
s/[/]+$//
t l2
:l2
s/^$/filesystem\/\
filesystem/p
t
h
s/^(.*)([/])([^/]+)$/\1\2\
\1\
\3/p
g
t l3
:l3
s/^.*[/]([^/]+)([.])([a-zA-Z0-9]+)$/\1\
\2\3\
\3/p
t
s/^.*[/](.+)$/\1/p
' <<<"$inputPath" | tr "\n" "$separatorChar")"
IFS="$oldIFS"
## Results (all use separatorChar=":")
## inputPath = /path/to/Foo.bar
## dirPathWithSlash = /path/to/
## dirPath = /path/to
## fileNameWithExt = Foo.bar
## fileName = Foo
## fileExtWithDot = .bar
## fileExt = bar
## inputPath = /path/to/Foobar
## dirPathWithSlash = /path/to/
## dirPath = /path/to
## fileNameWithExt = Foobar
## fileName = Foobar
## fileExtWithDot =
## fileExt =
## inputPath = /path/to/...bar
## dirPathWithSlash = /path/to/
## dirPath = /path/to
## fileNameWithExt = ...bar
## fileName = ..
## fileExtWithDot = .bar
## fileExt = bar
## inputPath = /path/to/..bar
## dirPathWithSlash = /path/to/
## dirPath = /path/to
## fileNameWithExt = ..bar
## fileName = .
## fileExtWithDot = .bar
## fileExt = bar
## inputPath = /path/to/.bar
## dirPathWithSlash = /path/to/
## dirPath = /path/to
## fileNameWithExt = .bar
## fileName = .bar
## fileExtWithDot =
## fileExt =
## inputPath = /path/to/...
## dirPathWithSlash = /path/to/
## dirPath = /path/to
## fileNameWithExt = ...
## fileName = ...
## fileExtWithDot =
## fileExt =
## inputPath = /path/to/Foo.
## dirPathWithSlash = /path/to/
## dirPath = /path/to
## fileNameWithExt = Foo.
## fileName = Foo.
## fileExtWithDot =
## fileExt =
## inputPath = / (the root directory)
## dirPathWithSlash = filesystem/
## dirPath = filesystem
## fileNameWithExt =
## fileName =
## fileExtWithDot =
## fileExt =
## inputPath = (invalid because empty)
## dirPathWithSlash =
## dirPath =
## fileNameWithExt =
## fileName =
## fileExtWithDot =
## fileExt =
## inputPath = Foo/bar (invalid because doesn't start with a forward slash)
## dirPathWithSlash =
## dirPath =
## fileNameWithExt =
## fileName =
## fileExtWithDot =
## fileExt =
_
Voilà comment cela fonctionne:
sed
analyse le chemin d'entrée et imprime les composants de chemin suivants dans l'ordre, sur des lignes distinctes:
tr
convertit la sortie sed
en une chaîne de séparation délimitée par des caractères des composants de chemin ci-dessus.
read
utilise le caractère de séparation comme séparateur de champ (_IFS="$separatorChar"
_) et affecte chacun des composants de chemin à sa variable respective.
Voici comment fonctionne la construction sed
:
s/^[[:space:]]+//
et s/[[:space:]]+$//
supprime les espaces blancs de début et/ou de fint l1
et :l1
actualise la fonction t
pour la prochaine fonction s
s/^([^/]|$)//
et t
recherche un chemin d'entrée non valide (qui ne commence pas par une barre oblique), auquel cas il laisse toutes les lignes de sortie sont vides et ferme la commande sed
s/[/]+$//
supprime les barres obliques finalest l2
et :l2
actualise la fonction t
pour la prochaine fonction s
s/^$/filesystem\/\\[newline]filesystem/p
et t
teste le cas particulier où le chemin d'entrée est constitué du répertoire racine /, auquel cas il imprime le système de fichiers/ et le système de fichiers pour le dirPathWithSlash et dirPath lignes de sortie, laisse toutes les autres lignes de sortie vides et quitte le sed commandeh
enregistre le chemin d'entrée dans l'espace d'attentes/^(.*)([/])([^/]+)$/\1\2\\[newline]\1\\[newline]\3/p
affiche le dirPathWithSlash , dirPath et fileNameWithExt lignes de sortieg
récupère le chemin d'entrée dans l'espace d'attentet l3
et :l3
actualise la fonction t
pour la prochaine fonction s
s/^.*\[/]([^/]+)([.])([a-zA-Z0-9]+)$/\1\\[newline]\2\3\\[newline]\3/p
et t
imprime le nom_fichier , fileExtWithDot , et fileExt lignes de sortie pour le cas où une extension de fichier existe (supposée être composée uniquement de caractères alphanumériques), puis quitte la commande sed
s/^.*\[/](.+)$/\1/p
imprime le nom_fichier mais pas le fileExtWithDot , et fileExt lignes de sortie pour le cas où une extension de fichier n'existe pas, puis quitte la commande sed
.