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`
C'est faux car cela ne fonctionne pas si le nom de fichier contient plusieurs .
caractères. Si, disons, je l'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ère ne pas lancer un interpréteur Python juste pour cela, si possible.
De meilleures idées?
extension="{$filename##*.}"
comme je l'ai fait pendant un certain temps! Déplacez l'$
extérieur des curlys: Droite:extension="${filename##*.}"
os.path.splitext
comme ci-dessus à la place ...Réponses:
Tout d'abord, obtenez le nom du fichier sans le chemin:
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:
Vous voudrez peut-être vérifier la documentation:
la source
basename
extension=$([[ "$filename" = *.* ]] && echo ".${filename##*.}" || echo '')
. Notez que si une extension est présente, elle sera retournée, y compris l'initiale.
, par exemple.txt
.Pour plus de détails, voir Extension des paramètres du shell dans le manuel de Bash.
la source
dinosaurs.in.tar
et que vous l'avez compressé pourdinosaurs.in.tar.gz
:)x.tar.gz
l'extension n'est pasgz
et le nom de fichier estx.tar
celui-ci. Il n'y a pas de double extension. je suis presque sûr que boost :: filesystem le gère de cette façon. (split path, change_extension ...) et son comportement est basé sur python si je ne me trompe pas.Habituellement, vous connaissez déjà l'extension, vous pouvez donc utiliser:
par exemple:
et nous obtenons
la source
basename
est tout à fait.zip
ou.ZIP
. Y a-t-il un moyen de faire quelque chose comme çabasename $file {.zip,.ZIP}
?Vous pouvez utiliser la magie de l'expansion des paramètres POSIX:
Il y a une mise en garde: si votre nom de fichier était de la forme,
./somefile.tar.gz
alorsecho ${FILENAME%%.*}
supprimerait avidement la correspondance la plus longue avec le.
et vous auriez la chaîne vide.(Vous pouvez contourner cela avec une variable temporaire:
)
Ce site explique plus.
la source
cut
n'a pas--complement
etsed
n'a pas-r
.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 utilise uniquement des fonctions internes et gère plus (mais pas tous) les noms de fichiers pathologiques.
Et voici quelques testcases:
la source
dir="${fullpath:0:${#fullpath} - ${#filename}}"
je l'ai souvent vudir="${fullpath%$filename}"
. C'est plus simple à écrire. Je ne sais pas s'il y a une réelle différence de vitesse ou des accrochages.which bash
->/bin/bash
; c'est peut-être votre distribution?Vous pouvez utiliser
basename
.Exemple:
Vous devez fournir le nom de base avec l'extension à supprimer, mais si vous exécutez toujours
tar
avec,-z
vous savez que l'extension le sera.tar.gz
.Cela devrait faire ce que vous voulez:
la source
cd $(basename $1 .tar.gz)
fonctionne pour les fichiers .gz. Mais en question, il a mentionnéArchive files have several extensions: tar.gz, tat.xz, tar.bz2
fonctionne bien, vous pouvez donc simplement utiliser:
Soit dit en passant, les commandes fonctionnent comme suit.
La commande pour
NAME
substitue un"."
caractère suivi d'un nombre quelconque de non-"."
caractères jusqu'à la fin de la ligne, sans rien (c'est-à-dire qu'elle supprime tout de la finale"."
à la fin de la ligne, inclus). Il s'agit essentiellement d'une substitution non gourmande utilisant la ruse regex.La commande pour
EXTENSION
substitue un nombre quelconque de caractères suivi d'un"."
caractère au début de la ligne, sans rien (c'est-à-dire qu'elle supprime tout du début de la ligne au point final, inclus). Il s'agit d'une substitution gourmande qui est l'action par défaut.la source
sed 's,\.[^\.]*$,,'
pour le nom etsed 's,.*\.,., ;t ;g'
pour l'extension (utilise les commandes atypiquestest
etget
, ainsi que lasubstitute
commande typique ).Mellen écrit dans un commentaire sur un article de blog:
En utilisant Bash, il y a aussi
${file%.*}
pour obtenir le nom de fichier sans l'extension et${file##*.}
pour obtenir l'extension seule. C'est,Les sorties:
la source
Pas besoin de s'embêter avec
awk
oused
ou mêmeperl
pour cette tâche simple. Il existe uneos.path.splitext()
solution entièrement compatible avec Bash qui n'utilise que des extensions de paramètres.Implémentation de référence
Documentation de
os.path.splitext(path)
:Code Python:
Implémentation de Bash
Honorer les périodes marquantes
Ignorer les périodes principales
Les tests
Voici des cas de test pour l' implémentation Ignorer les périodes principales , qui doivent correspondre à l'implémentation de référence Python sur chaque entrée.
Résultats de test
Tous les tests ont réussi.
la source
text.tar.gz
devrait êtretext
et l'extension être.tar.gz
os.path.splitext
Python. La question de savoir si cette mise en œuvre est raisonnable pour des contributions éventuellement controversées est un autre sujet."$root"
)? Que pourrait-il arriver s'ils étaient omis? (Je n'ai trouvé aucune documentation à ce sujet.) Comment cela gère-t-il les noms de fichiers avec*
ou?
en eux?*
-à- dire et?
ne sont pas spéciaux. Les deux parties de ma question se répondent donc. Ai-je raison de dire que cela n'est pas documenté? Ou est-ce censé être compris du fait que les guillemets désactivent l'expansion globale en général?root="${path#?}";root="${path::1}${root%.*}"
- puis procédez de même pour extraire l'extension.Vous pouvez utiliser la
cut
commande pour supprimer les deux dernières extensions (la".tar.gz"
partie):Comme l'a noté Clayton Hughes dans un commentaire, cela ne fonctionnera pas pour l'exemple réel de la question. Donc, comme alternative, je propose d'utiliser
sed
des expressions régulières étendues, comme ceci:Il fonctionne en supprimant inconditionnellement les deux dernières extensions (alphanumériques).
[Mis à jour à nouveau après le commentaire d'Anders Lindahl]
la source
$
pour vérifier que l'extension correspondante se trouve à la fin du nom de fichier. Sinon, un nom de fichier commei.like.tar.gz.files.tar.bz2
pourrait produire un résultat inattendu.sed
ordre des chaînes. Même avec$
à la fin un nom de fichier tel quempc-1.0.1.tar.bz2.tar.gz
va supprimer les deux.tar.gz
et ensuite.tar.bz2
.Voici quelques suggestions alternatives (principalement en
awk
), y compris des cas d'utilisation avancés, comme l'extraction des numéros de version pour les packages logiciels.Tous les cas d'utilisation utilisent le chemin complet d'origine comme entrée, sans dépendre des résultats intermédiaires.
la source
La réponse acceptée fonctionne bien dans les typiques cas , mais échoue bord des cas , à savoir:
extension=${filename##*.}
renvoie le nom de fichier d'entrée plutôt qu'une chaîne vide.extension=${filename##*.}
ne comprend pas l'initiale.
, contrairement à la convention..
ne fonctionnerait pas pour les noms de fichiers sans suffixe.filename="${filename%.*}"
sera la chaîne vide, si le nom du fichier d'entrée commence par.
et ne contient aucun autre.
caractère (par exemple,.bash_profile
) - contrairement à la convention.---------
Ainsi, la complexité d'une solution robuste qui couvre tous les cas marginaux appelle une fonction - voir sa définition ci-dessous; il peut renvoyer tous les composants d'un chemin .
Exemple d'appel:
Notez que les arguments après le chemin d'entrée sont des noms de variables de position librement choisis .
Pour ignorer les variables sans intérêt qui précèdent celles qui le sont, spécifiez
_
(pour utiliser la variable à jeter$_
) ou''
; par exemple, pour extraire uniquement la racine et l'extension du nom de fichier, utilisezsplitPath '/etc/bash.bashrc' _ _ fnameroot extension
.Code de test qui exerce la fonction:
Sortie attendue - notez les cas de bord:
.
( non considéré comme le début du suffixe)/
(fin/
est ignorée).
est renvoyé comme chemin parent).
un jeton préfixé (seul le dernier est considéré comme le suffixe):la source
La solution la plus petite et la plus simple (en une seule ligne) est:
la source
echo
. En général, ilecho $(command)
vaut mieux écrire simplement,command
sauf si vous avez spécifiquement besoin du shell pour effectuer une tokenisation des espaces blancs et une expansion générique sur la sortiecommand
avant d'afficher le résultat. Quiz: quelle est la sortie deecho $(echo '*')
(et si c'est ce que vous voulez vraiment, vous voulez vraiment vraiment justeecho *
).echo
commande du tout. Je viens de l'utiliser pour démontrer le résultatfoo
qui apparaît sur la 3e ligne comme résultat de la 2e ligne.basename "${file%.*}"
ferait la même chose; vous utilisez une substitution de commande pour capturer sa sortie, uniquement surecho
cette même sortie immédiatement. (Sans citer, le résultat est nominalement différent; mais ce n'est guère pertinent, encore moins une fonctionnalité, ici.)basename "$file" .txt
Evite également la complexité de la substitution de paramètres.Je pense que si vous avez juste besoin du nom du fichier, vous pouvez essayer ceci:
Et c'est tout = D.
la source
Vous pouvez forcer la coupure pour afficher tous les champs et les suivants en ajoutant
-
au numéro de champ.Donc, si FILE est
eth0.pcap.gz
, l'extension serapcap.gz
En utilisant la même logique, vous pouvez également récupérer le nom de fichier en utilisant '-' avec cut comme suit:
Cela fonctionne même pour les noms de fichiers sans extension.
la source
Reconnaissance magique des fichiers
En plus des nombreuses bonnes réponses à cette question sur le débordement de pile, je voudrais ajouter:
Sous Linux et autres Unixen, il existe une commande magique nommée
file
, qui fait la détection de type de fichier en analysant les premiers octets de fichier. Il s'agit d'un outil très ancien, initialement utilisé pour les serveurs d'impression (s'il n'est pas créé pour ... je n'en suis pas sûr).Des extensions de standards peuvent être trouvées dans
/etc/mime.types
(sur mon bureau Debian GNU / Linux. Voirman file
etman mime.types
. Peut-être devez-vous installer l'file
utilitaire et lesmime-support
paquets):Vous pouvez créer un frapperfonction pour déterminer l'extension droite. Il y a un petit échantillon (pas parfait):
Cette fonction pourrait définir une variable Bash pouvant être utilisée ultérieurement:
(Ceci est inspiré de la bonne réponse @Petesh):
la source
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
.Cela fonctionne pour moi:
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 aide pour toute personne ayant le même problème =)la source
os.path.splitext
, qui est ce que le PO souhaite) est('stuff.tar', '.gz')
.J'utilise le script suivant
la source
Cela prend en charge plusieurs points et espaces dans un nom de fichier, mais s'il n'y a pas d'extension, il renvoie le nom de fichier lui-même. Facile à vérifier cependant; testez simplement que 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 géré en deux étapes. Si l'extension est gz, vérifiez à nouveau pour voir s'il existe également une extension tar.
la source
Comment extraire le nom de fichier et l'extension dans le poisson :
Avertissements: fractionnements sur le dernier point, ce qui fonctionne bien pour les noms de fichiers contenant des points, mais pas bien pour les extensions contenant des points. Voir l'exemple ci-dessous.
Usage:
Il y a probablement de meilleures façons de le faire. N'hésitez pas à modifier ma réponse pour l'améliorer.
S'il y a un ensemble limité d'extensions que vous allez traiter et que vous les connaissez toutes, essayez ceci:
Cela n'a pas la mise en garde comme premier exemple, mais vous devez gérer chaque cas, donc cela pourrait être plus fastidieux en fonction du nombre d'extensions que vous pouvez attendre.
la source
Voici le code avec AWK . Cela peut être fait plus simplement. Mais je ne suis pas bon en AWK.
la source
split()
.awk -F / '{ n=split($2, a, "."); print a[n] }' uses
/ `comme délimiteur de niveau supérieur, mais divise ensuite les seconds champs.
et imprime le dernier élément du nouveau tableau.Utilisez simplement
${parameter%word}
Dans ton cas:
Si vous voulez le tester, tous les travaux suivants, et supprimez simplement l'extension:
la source
=
panneaux.Construire à partir de la réponse Petesh , si seul le nom de fichier est nécessaire, le chemin et l'extension peuvent être supprimés sur une seule ligne,
la source
filename="$(basename "${fullname%.*}")"
basename
est facultatif, mais spécifie l'extension à supprimer. La substitution peut toujours être utile, mais peut-être pasbasename
vraiment, car vous pouvez réellement effectuer toutes ces substitutions avec les commandes internes du shell.Basé en grande partie sur l'excellent et le plein de bashismes aléatoires et utiles de @ mklement0 - ainsi que d'autres réponses à cette / d'autres questions / "ce sacrément Internet" ... J'ai résumé le tout dans un petit peu, fonction réutilisable pour mon (ou votre)
.bash_profile
qui s'occupe de ce que (je considère) devrait être une version plus robuste dedirname
/basename
/ ce que vous avez ..Exemples d'utilisation ...
la source
$IFS
tout (et si vous l'étiez, vous pourriez utiliserlocal
pour localiser l'effet du réglage). - Mieux vaut utiliser deslocal
variables. - Votre message d'erreur doit être envoyé àstderr
, passtdout
(utiliser1>&2
) et vous devez renvoyer un code de sortie différent de zéro. - Il vaut mieux renommerfullname
àbasename
(l'ancien chemin suggère un des composants dir). -name
ajoute inconditionnellement un.
(point), même si l'original n'en avait pas. Vous pouvez simplement utiliser l'basename
utilitaire, mais notez qu'il ignore une terminaison/
.Une réponse simple:
Pour développer la réponse des variables POSIX , notez que vous pouvez faire des modèles plus intéressants. Donc, pour le cas détaillé ici, vous pouvez simplement faire ceci:
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> . <something-else> alors
devrait bien fonctionner.
Le lien de la réponse ci-dessus semble être mort. Voici une excellente explication d'un tas de la manipulation de chaînes que vous pouvez faire directement dans Bash, à partir de TLDP .
la source
Si vous souhaitez également autoriser les extensions vides , c'est la plus courte que j'ai pu trouver:
1ère ligne expliquée: elle correspond à PATH.EXT ou à TOUT et la remplace par EXT. Si N'IMPORTE QUOI correspond, le groupe ext n'est pas capturé.
la source
C'est le seul qui a fonctionné pour moi:
Cela peut également être utilisé dans l'interpolation de chaînes, mais malheureusement, vous devez le définir
base
au préalable.la source
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 en cas de conflit de noms par rapport à la casse.
Le test.
FYI: Le programme complet de translittération et plus de cas de test peuvent être trouvés ici: https://www.dropbox.com/s/4c6m0f2e28a1vxf/avoid-clashes-code.zip?dl=0
la source
extension=$([[ "$theFileName" == *.* ]] && echo ".${theFileName##*.}" || echo '')
En utilisant un fichier d'exemple
/Users/Jonathan/Scripts/bash/MyScript.sh
, ce code:se traduira par
${ME}
êtreMyScript
et${MY_EXT}
être.sh
:Scénario:
Quelques tests:
la source
basename
est peut-être exagéré.D'après les réponses ci-dessus, le plus court oneliner pour imiter Python
en supposant que votre fichier a vraiment une extension, est
la source
EXT
supprimer avant de savoir quoi mettre, donc c'est des tortues tout le long. (De plus, vous devez éviter tout en majuscule pour vos noms de variables privées; ils sont réservés aux variables système.)