web-dev-qa-db-fra.com

Extraire le fichier nom de base sans chemin et extension en bash

Noms de fichiers donnés comme ceux-ci:

/the/path/foo.txt
bar.txt

J'espère avoir:

foo
bar

Pourquoi ça ne marche pas?

#!/bin/bash

fullfile=$1
fname=$(basename $fullfile)
fbname=${fname%.*}
echo $fbname

Quelle est la bonne façon de le faire?

287
neversaint

Vous n'êtes pas obligé d'appeler la commande externe basename. À la place, vous pouvez utiliser les commandes suivantes:

$ s=/the/path/foo.txt
$ echo ${s##*/}
foo.txt
$ s=${s##*/}
$ echo ${s%.txt}
foo
$ echo ${s%.*}
foo

Notez que cette solution devrait fonctionner dans tous les récents ( post 2004 ) POSIX coques compatibles, (par exemple bash, dash, ksh, etc.).

Source: Expansion des paramètres du langage de commande Shell 2.6.2

Plus d'informations sur les manipulations de chaîne bash: http://tldp.org/LDP/LG/issue18/bash.html

575
ghostdog74

La commande nom_base a deux invocations différentes; dans l'un, vous spécifiez uniquement le chemin, auquel cas il vous donne le dernier composant, tandis que dans l'autre, vous donnez également un suffixe qu'il supprimera. Ainsi, vous pouvez simplifier votre exemple de code en utilisant la deuxième invocation de basename. Veillez également à citer correctement les choses:

 fbname = $ (nom de base "$ 1" .txt) 
 echo "$ fbname" 
263

Une combinaison du nom de base et de la coupe fonctionne bien, même en cas de fin double comme .tar.gz:

fbname=$(basename "$fullfile" | cut -d. -f1)

Ce serait intéressant si cette solution nécessitait moins de puissance arithmétique que Bash Parameter Expansion.

54
kom lim

Pure bash, pas de basename, pas de jonglerie variable. Définissez une chaîne et echo:

s=/the/path/foo.txt
echo ${s//+(*\/|.*)}

Sortie:

foo

Remarque: l’option bash extglob doit être "on", (bunt définit extglob "on" par défaut), si ce n'est pas le cas, faites:

shopt -s extglob

En parcourant la ${s//+(*\/|.*)}:

  1. ${s - commencez par $ s.
  2. // substitue chaque instance du motif.
  3. +( correspond à n ou plusieurs de la liste de modèles entre parenthèses, (i.e. jusqu'au point 7 ci-dessous).
  4. 1st ​​pattern: *\/ correspond à tout ce qui précède un caractère "/" littéral.
  5. modèle séparateur | qui, dans ce cas, agit comme un OU logique .
  6. 2nd pattern: .* ne fait rien après un "." littéral - c'est-à-dire que, dans bash, le "." n'est qu'un point char, et not ​​ n point regex .
  7. ) end liste de modèles.
  8. } fin le développement des paramètres. Avec une substitution de chaîne, il y a généralement un autre /, suivi d'une chaîne de remplacement. Mais comme il n'y a pas de / ici, les modèles correspondants ne sont remplacés par rien; cela supprime les correspondances.

Arrière-plan man bash pertinent:

  1. substitution de modèle:
  ${parameter/pattern/string}
          Pattern substitution.  The pattern is expanded to produce a pat‐
          tern just as in pathname expansion.  Parameter is  expanded  and
          the  longest match of pattern against its value is replaced with
          string.  If pattern begins with /, all matches  of  pattern  are
          replaced   with  string.   Normally  only  the  first  match  is
          replaced.  If pattern begins with #, it must match at the begin‐
          ning of the expanded value of parameter.  If pattern begins with
          %, it must match at the end of the expanded value of  parameter.
          If string is null, matches of pattern are deleted and the / fol‐
          lowing pattern may be omitted.  If parameter is @ or *, the sub‐
          stitution  operation  is applied to each positional parameter in
          turn, and the expansion is the resultant list.  If parameter  is
          an  array  variable  subscripted  with  @ or *, the substitution
          operation is applied to each member of the array  in  turn,  and
          the expansion is the resultant list.
  1. correspondance de motif étendue:
  If the extglob Shell option is enabled using the shopt builtin, several
   extended  pattern  matching operators are recognized.  In the following
   description, a pattern-list is a list of one or more patterns separated
   by a |.  Composite patterns may be formed using one or more of the fol‐
   lowing sub-patterns:

          ?(pattern-list)
                 Matches zero or one occurrence of the given patterns
          *(pattern-list)
                 Matches zero or more occurrences of the given patterns
          +(pattern-list)
                 Matches one or more occurrences of the given patterns
          @(pattern-list)
                 Matches one of the given patterns
          !(pattern-list)
                 Matches anything except one of the given patterns
18
agc

Voici les oneliners:

  1. $(basename ${s%.*})
  2. $(basename ${s} .${s##*.})

J'avais besoin de ça, comme l'ont demandé bongbang et w4etwetewtwet.

15
sancho.s

Voici un autre moyen (plus complexe) d'obtenir le nom de fichier ou l'extension. Commencez par utiliser la commande rev pour inverser le chemin du fichier. Coupez-le à partir du premier ., puis inversez-le à nouveau, comme ceci: :

filename=`rev <<< "$1" | cut -d"." -f2- | rev`
fileext=`rev <<< "$1" | cut -d"." -f1 | rev`
9
higuaro

Si vous voulez jouer à Nice avec les chemins de fichiers Windows (sous Cygwin), vous pouvez aussi essayer ceci:

fname=${fullfile##*[/|\\]}

Cela tiendra compte des séparateurs de barre oblique inverse lors de l'utilisation de BaSH sous Windows.

2
Apelsin

Juste une alternative que j'ai imaginé pour extraire une extension, en utilisant les posts de ce fil avec ma propre petite base de connaissances qui m’était plus familière.

ext="$(rev <<< "$(cut -f "1" -d "." <<< "$(rev <<< "file.docx")")")"

Note: S'il vous plaît conseiller sur mon utilisation des guillemets; cela a fonctionné pour moi mais il se peut que je manque quelque chose sur leur bon usage (j'en utilise probablement trop).

0
Diomoid