Étant donné un chemin de fichier de chaîne tel que /foo/fizzbuzz.bar
, comment pourrais-je utiliser bash pour extraire uniquement la partie fizzbuzz
de ladite chaîne?
Voici comment procéder avec les opérateurs # et% de Bash.
$ x="/foo/fizzbuzz.bar"
$ y=${x%.bar}
$ echo ${y##*/}
fizzbuzz
${x%.bar}
pourrait également être ${x%.*}
pour tout supprimer après un point ou ${x%%.*}
pour 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 peut être trouvée dans le manuel de Bash . Recherchez ${parameter%Word}
et ${parameter%%Word}
section de correspondance de portion finale.
regardez la commande basename:
NAME=$(basename /foo/fizzbuzz.bar .bar)
Pure bash, effectué 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'
Supprimer l'extension d'une chaîne de chemin:
base=${file%.*}
#${base} is now 'file'.
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 contient des points dans son nom de fichier (devant son extension); le programme basename
nécessite cependant, mais cela fait partie du GNU coreutils, il devrait donc être livré avec n’importe quelle distribution.
Façon pure bash:
~$ x="/foo/bar/fizzbuzz.bar.quux.zoom";
~$ y=${x/\/*\//};
~$ echo ${y/.*/};
fizzbuzz
Cette fonctionnalité est expliquée dans man bash sous "Extension de paramètres". Les moyens non bash abondent: awk, Perl, sed, etc.
EDIT: Fonctionne avec des points dans le fichier suffixes et n'a pas besoin de connaître le suffixe (extension), mais ne le fait pas fonctionne avec des 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 la sortie:
basename: fizzbuzz.bar
basename + remove .bar: fizzbuzz
dirname: /foo
En plus de syntaxe conforme à POSIX utilisée dans cette réponse ,
basename string [suffix]
un péché
basename /foo/fizzbuzz.bar .bar
GNU basename
supporte une autre syntaxe:
basename -s .bar /foo/fizzbuzz.bar
avec le même résultat. La différence et l'avantage est que -s
implique -a
, qui prend en charge plusieurs arguments:
$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar
Vous pouvez même sécuriser votre nom de fichier en séparant la sortie avec des octets NUL à l'aide de l'option -z
, 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 publications, vous pouvez toujours utiliser sed. Voici un exemple (laid). Ce n'est pas le meilleur, 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 va vous obtenir la sortie
fizzbuzz
Utiliser basename
suppose que vous sachiez quelle est l'extension du fichier, n'est-ce pas?
Et je crois que les diverses suggestions d’expression régulière ne s’appliquent pas à 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 un "/" eux-mêmes (juste pour des 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";
Perl -pe 's/\..*$//;s{^.*/}{}'
Faites attention à la solution Perl suggérée: elle supprime tout ce qui se trouve 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. Il supprimera également le suffixe s'il est indiqué et s'il correspond au suffixe du fichier, mais vous devez connaître le suffixe à attribuer à la commande. Sinon, vous pouvez utiliser mv et déterminer ce que le nouveau nom doit être autrement.
En combinant 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