Comment supprimer le suffixe de fichier et la partie chemin d'une chaîne de chemin dans Bash?
396
Étant donné un chemin de fichier de chaîne tel que /foo/fizzbuzz.bar, comment utiliserais-je bash pour extraire uniquement la fizzbuzzpartie de ladite chaîne?
Informations que vous pouvez trouver dans le manuel de Bash , recherchez ${parameter%word}et ${parameter%%word}dans la section de correspondance des portions de fin.
1ac0
Réponses:
574
Voici comment le faire avec les opérateurs # et% dans Bash.
J'ai fini par utiliser celui-ci parce qu'il était le plus flexible et il y avait quelques autres choses similaires que je voulais faire aussi bien que cela a bien fonctionné.
Lawrence Johnston
C'est probablement la plus flexible de toutes les réponses publiées, mais je pense que les réponses suggérant les commandes basename et dirname méritent également une certaine attention. Ils peuvent être juste l'astuce si vous n'avez besoin d'aucune autre correspondance de motifs fantaisie.
mgadda
7
Comment s'appelle $ {x% .bar}? J'aimerais en savoir plus.
Basil
14
@Basil: Expansion des paramètres. Sur une console, tapez "man bash" puis tapez "/ paramètre expansion"
Zan Lynx
1
Je suppose que l'explication «man bash» a du sens si vous savez déjà ce qu'elle fait ou si vous l'avez essayé vous-même à la dure. C'est presque aussi mauvais que git reference. Je préfère le rechercher sur Google.
Probablement la plus simple de toutes les solutions actuellement proposées ... bien que j'utiliserais $ (...) au lieu de backticks.
Michael Johnson,
6
Le plus simple mais ajoute une dépendance (pas énorme ou bizarre, je l'avoue). Il doit également connaître le suffixe.
Vinko Vrsalovic
Et peut être utilisé pour supprimer quoi que ce soit de la fin, en gros, il ne fait que retirer la chaîne de la fin.
Smar
2
Le problème est le temps frappé. Je viens de rechercher la question pour cette discussion après avoir regardé bash prendre près de 5 minutes pour traiter 800 fichiers, en utilisant basename. En utilisant la méthode d'expression régulière ci-dessus, le temps a été réduit à environ 7 secondes. Bien que cette réponse soit plus facile à réaliser pour le programmeur, le temps atteint est tout simplement trop. Imaginez un dossier contenant quelques milliers de fichiers! J'ai certains de ces dossiers.
xizdaqrian
@xizdaqrian C'est absolument faux. Il s'agit d'un programme simple, qui ne devrait pas prendre une demi-seconde pour revenir. Je viens d'exécuter time find / home / me / dev -name "* .py" .py -exec basename {} \; et il a supprimé l'extension et le répertoire de 1500 fichiers en 1 seconde au total.
Laszlo Treszkai
40
Pure bash, fait en deux opérations distinctes:
Supprimez le chemin d'une chaîne de chemin:
path=/foo/bar/bim/baz/file.gif
file=${path##*/} #$file is now 'file.gif'
En utilisant le nom de base, j'ai utilisé ce qui suit pour y parvenir:
for file in*;do
ext=${file##*.}
fname=`basename $file $ext`# Do things with $fnamedone;
Cela ne nécessite aucune connaissance a priori de l'extension de fichier et fonctionne même lorsque vous avez un nom de fichier qui a des points dans son nom de fichier (devant son extension); il nécessite le programme basename, mais cela fait partie des coreutils GNU donc il devrait être livré avec n'importe quelle distribution.
Cette fonctionnalité est expliquée dans man bash sous "Expansion des paramètres". Les moyens non bash abondent: awk, perl, sed et ainsi de suite.
EDIT: Fonctionne avec les points dans les suffixes de fichier et n'a pas besoin de connaître le suffixe (extension), mais ne fonctionne pas avec les points dans le nom lui-même.
Il serait utile de corriger la citation ici - peut-être exécutez-le via shellcheck.net avec mystring=$1plutôt que la valeur constante actuelle (qui supprimera plusieurs avertissements, étant certain de ne pas contenir d'espaces / caractères globaux / etc), et de résoudre les problèmes trouve?
Charles Duffy
Eh bien, j'ai apporté des modifications appropriées pour prendre en charge les guillemets dans $ mystring.
Mon Dieu
Serait une amélioration supplémentaire pour citer les résultats: echo "basename: $(basename "$mystring")"- de cette façon si mystring='/foo/*'vous n'obtenez pas le *remplacé par une liste de fichiers dans le répertoire en cours après labasename fin.
Charles Duffy
6
L'utilisation basenamesuppose que vous connaissez l'extension du fichier, n'est-ce pas?
Et je crois que les diverses suggestions d'expressions régulières ne font pas face à un nom de fichier contenant plus d'un "."
Ce qui suit semble faire face à des points doubles. Oh, et les noms de fichiers qui contiennent eux-mêmes un "/" (juste pour les coups de pied)
Pour paraphraser Pascal, "Désolé, ce script est si long. Je n'ai pas eu le temps de le raccourcir"
Cela peut même être rendu sûr pour le nom de fichier en séparant la sortie par des octets NUL en utilisant l' -zoption, par exemple pour ces fichiers contenant des blancs, des nouvelles lignes et des caractères globaux (cités par ls):
$ ls has*'has'$'\n''newline.bar''has space.bar''has*.bar'
Si vous ne pouvez pas utiliser le nom de base comme suggéré dans d'autres articles, vous pouvez toujours utiliser sed. Voici un exemple (moche). Ce n'est pas le plus grand, mais cela fonctionne en extrayant la chaîne souhaitée et en remplaçant l'entrée par la chaîne souhaitée.
echo '/foo/fizzbuzz.bar'| sed 's|.*\/\([^\.]*\)\(\..*\)$|\1|g'
Bien que ce soit la réponse à la question d'origine, cette commande est utile lorsque j'ai des lignes de chemins dans un fichier pour extraire les noms de base pour les imprimer à l'écran.
Sangcheol Choi
2
Méfiez-vous de la solution Perl suggérée: elle supprime tout après le premier point.
$ echo some.file.with.dots | perl -pe 's/\..*$//;s{^.*/}{}'
some
Si vous voulez le faire avec perl, cela fonctionne:
Le nom de base fait cela, supprime le chemin d'accès. Il supprimera également le suffixe s'il est donné et s'il correspond au suffixe du fichier, mais vous devrez connaître le suffixe à attribuer à la commande. Sinon, vous pouvez utiliser mv et déterminer quel devrait être le nouveau nom d'une autre manière.
${parameter%word}
et${parameter%%word}
dans la section de correspondance des portions de fin.Réponses:
Voici comment le faire avec les opérateurs # et% dans Bash.
${x%.bar}
pourrait également être${x%.*}
de tout supprimer après un point ou${x%%.*}
de tout supprimer après le premier point.Exemple:
La documentation se trouve dans le manuel de Bash . Rechercher
${parameter%word}
et${parameter%%word}
suivre la section de correspondance des portions.la source
regardez la commande basename:
la source
Pure bash, fait en deux opérations distinctes:
Supprimez le chemin d'une chaîne de chemin:
Supprimez l'extension d'une chaîne de chemin:
la source
En utilisant le nom de base, j'ai utilisé ce qui suit pour y parvenir:
Cela ne nécessite aucune connaissance a priori de l'extension de fichier et fonctionne même lorsque vous avez un nom de fichier qui a des points dans son nom de fichier (devant son extension); il nécessite le programme
basename
, mais cela fait partie des coreutils GNU donc il devrait être livré avec n'importe quelle distribution.la source
fname=`basename $file .$ext`
Voie bash pure:
Cette fonctionnalité est expliquée dans man bash sous "Expansion des paramètres". Les moyens non bash abondent: awk, perl, sed et ainsi de suite.
EDIT: Fonctionne avec les points dans les suffixes de fichier et n'a pas besoin de connaître le suffixe (extension), mais ne fonctionne pas avec les points dans le nom lui-même.
la source
Les fonctions basename et dirname sont ce que vous recherchez:
A une sortie:
la source
mystring=$1
plutôt que la valeur constante actuelle (qui supprimera plusieurs avertissements, étant certain de ne pas contenir d'espaces / caractères globaux / etc), et de résoudre les problèmes trouve?echo "basename: $(basename "$mystring")"
- de cette façon simystring='/foo/*'
vous n'obtenez pas le*
remplacé par une liste de fichiers dans le répertoire en cours après labasename
fin.L'utilisation
basename
suppose que vous connaissez l'extension du fichier, n'est-ce pas?Et je crois que les diverses suggestions d'expressions régulières ne font pas face à un nom de fichier contenant plus d'un "."
Ce qui suit semble faire face à des points doubles. Oh, et les noms de fichiers qui contiennent eux-mêmes un "/" (juste pour les coups de pied)
Pour paraphraser Pascal, "Désolé, ce script est si long. Je n'ai pas eu le temps de le raccourcir"
la source
En plus de la syntaxe conforme POSIX utilisée dans cette réponse ,
un péché
GNU
basename
prend en charge une autre syntaxe:avec le même résultat. La différence et l'avantage est que cela
-s
implique-a
, qui prend en charge plusieurs arguments:Cela peut même être rendu sûr pour le nom de fichier en séparant la sortie par des octets NUL en utilisant l'
-z
option, par exemple pour ces fichiers contenant des blancs, des nouvelles lignes et des caractères globaux (cités parls
):Lecture dans un tableau:
readarray -d
nécessite Bash 4.4 ou plus récent. Pour les anciennes versions, nous devons boucler:la source
la source
Si vous ne pouvez pas utiliser le nom de base comme suggéré dans d'autres articles, vous pouvez toujours utiliser sed. Voici un exemple (moche). Ce n'est pas le plus grand, mais cela fonctionne en extrayant la chaîne souhaitée et en remplaçant l'entrée par la chaîne souhaitée.
Qui vous donnera la sortie
la source
Méfiez-vous de la solution Perl suggérée: elle supprime tout après le premier point.
Si vous voulez le faire avec perl, cela fonctionne:
Mais si vous utilisez Bash, les solutions avec
y=${x%.*}
(oubasename "$x" .ext
si vous connaissez l'extension) sont beaucoup plus simples.la source
Le nom de base fait cela, supprime le chemin d'accès. Il supprimera également le suffixe s'il est donné et s'il correspond au suffixe du fichier, mais vous devrez connaître le suffixe à attribuer à la commande. Sinon, vous pouvez utiliser mv et déterminer quel devrait être le nouveau nom d'une autre manière.
la source
Combiner la réponse la mieux notée avec la deuxième réponse la mieux notée pour obtenir le nom de fichier sans le chemin complet:
la source