Étant donné un chemin de fichier de chaîne tel que /foo/fizzbuzz.bar
, comment utiliserais-je bash pour extraire uniquement la fizzbuzz
partie de ladite chaîne?
Étant donné un chemin de fichier de chaîne tel que /foo/fizzbuzz.bar
, comment utiliserais-je bash pour extraire uniquement la fizzbuzz
partie de ladite chaîne?
Réponses:
Voici comment le faire avec les opérateurs # et% dans Bash.
$ x="/foo/fizzbuzz.bar"
$ y=${x%.bar}
$ echo ${y##*/}
fizzbuzz
${x%.bar}
pourrait également être ${x%.*}
de tout supprimer après un point ou ${x%%.*}
de tout supprimer après le premier point.
Exemple:
$ x="/foo/fizzbuzz.bar.quux"
$ y=${x%.*}
$ echo $y
/foo/fizzbuzz.bar
$ y=${x%%.*}
$ echo $y
/foo/fizzbuzz
La documentation se trouve dans le manuel de Bash . Rechercher ${parameter%word}
et ${parameter%%word}
suivre la section de correspondance des portions.
regardez la commande basename:
NAME="$(basename /foo/fizzbuzz.bar .bar)"
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 $fname
done;
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.
fname=`basename $file .$ext`
Voie bash pure:
~$ x="/foo/bar/fizzbuzz.bar.quux.zoom";
~$ y=${x/\/*\//};
~$ echo ${y/.*/};
fizzbuzz
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.
Les fonctions basename et dirname sont ce que vous recherchez:
mystring=/foo/fizzbuzz.bar
echo basename: $(basename "${mystring}")
echo basename + remove .bar: $(basename "${mystring}" .bar)
echo dirname: $(dirname "${mystring}")
A une sortie:
basename: fizzbuzz.bar
basename + remove .bar: fizzbuzz
dirname: /foo
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 si mystring='/foo/*'
vous n'obtenez pas le *
remplacé par une liste de fichiers dans le répertoire en cours après la basename
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"
#!/usr/bin/perl
$fullname = $ARGV[0];
($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
print $basename . "\n";
En plus de la syntaxe conforme POSIX utilisée dans cette réponse ,
basename string [suffix]
un péché
basename /foo/fizzbuzz.bar .bar
GNUbasename
prend en charge une autre syntaxe:
basename -s .bar /foo/fizzbuzz.bar
avec le même résultat. La différence et l'avantage est que cela -s
implique -a
, qui prend en charge plusieurs arguments:
$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar
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 par ls
):
$ ls has*
'has'$'\n''newline.bar' 'has space.bar' 'has*.bar'
Lecture dans un tableau:
$ readarray -d $'\0' arr < <(basename -zs .bar has*)
$ declare -p arr
declare -a arr=([0]=$'has\nnewline' [1]="has space" [2]="has*")
readarray -d
nécessite Bash 4.4 ou plus récent. Pour les anciennes versions, nous devons boucler:
while IFS= read -r -d '' fname; do arr+=("$fname"); done < <(basename -zs .bar has*)
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'
Qui vous donnera la sortie
fizzbuzz
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:
$ echo some.file.with.dots | perl -pe 's/(.*)\..*$/$1/;s{^.*/}{}'
some.file.with
Mais si vous utilisez Bash, les solutions avec y=${x%.*}
(ou basename "$x" .ext
si vous connaissez l'extension) sont beaucoup plus simples.
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.
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:
$ x="/foo/fizzbuzz.bar.quux"
$ y=(`basename ${x%%.*}`)
$ echo $y
fizzbuzz
${parameter%word}
et${parameter%%word}
dans la section de correspondance des portions de fin.