Comment puis-je supprimer l'extension d'un nom de fichier dans un script shell?


147

Quel est le problème avec le code suivant?

name='$filename | cut -f1 -d'.''

Tel quel, j'obtiens la chaîne littérale $filename | cut -f1 -d'.', mais si je supprime les guillemets, je n'obtiens rien. Pendant ce temps, en tapant

"test.exe" | cut -f1 -d'.'

dans une coquille me donne la sortie que je veux, test. Je sais déjà qu'on lui $filenamea attribué la bonne valeur. Ce que je veux faire, c'est attribuer à une variable le nom de fichier sans l'extension.


6
basename $filename .exeferait la même chose. Cela suppose que vous sachiez toujours quelle extension vous souhaitez supprimer.
mpe

7
@mpe, tu veux dire basename "$filename" .exe. Sinon, les noms de fichiers avec des espaces seraient de mauvaises nouvelles.
Charles Duffy

Réponses:


114

Vous devez utiliser la syntaxe de substitution de commande$(command) lorsque vous souhaitez exécuter une commande dans script / commande.

Donc votre ligne serait

name=$(echo "$filename" | cut -f 1 -d '.')

Explication du code:

  1. echoobtenir la valeur de la variable $filenameet l'envoyer à la sortie standard
  2. Nous récupérons ensuite la sortie et la dirigons vers la cutcommande
  3. Le cututilisera le. comme délimiteur (également connu sous le nom de séparateur) pour couper la chaîne en segments et en -fsélectionnant le segment que nous voulons avoir en sortie
  4. Ensuite, la $()substitution de commande obtiendra la sortie et retournera sa valeur
  5. La valeur retournée sera affectée à la variable nommée name

Notez que cela donne la partie de la variable jusqu'à la première période .:

$ filename=hello.world
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello.hello.hello
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello
$ echo "$filename" | cut -f 1 -d '.'
hello

Merci. J'ai également remarqué que je devais utiliser la commande echo. name =echo $filename | cut -f1 -d'.'
mimicocotopus

17
Les backticks sont obsolètes par POSIX, $()c'est préférable.
jordanm

3
La bifurcation et la tuyauterie pour atteindre quelques caractères est la pire solution imaginable.
Jens

39
Le problème avec cette réponse est qu'elle suppose que la chaîne d'entrée n'a qu'un seul point ... @chepner ci-dessous a une bien meilleure solution ... name = $ {filename%. *}
Scott Stensland

4
Cette réponse est caractéristique des débutants et ne doit pas être répandue. Utilisez le mécanisme intégré tel que décrit par la réponse de
Chepner

261

Vous pouvez également utiliser l'expansion des paramètres:

$ filename=foo.txt
$ echo "${filename%.*}"
foo

6
ici l'explication de la commande: gnu.org/software/bash/manual/html_node
Carlos Robles

1
Et là, j'étais sur le point de l'utiliser echo -n "This.File.Has.Periods.In.It.txt" | awk -F. '{$NF=""; print $0}' | tr ' ' '.' | rev | cut -c 2- | rev. Merci.
user208145

1
Cela fonctionne-t-il avec des fichiers avec plusieurs extensions comme image.png.gz?
Hawker65

11
%.*supprimera uniquement la dernière extension; si vous souhaitez supprimer toutes les extensions, utilisez %%.*.
chepner

1
Cela semble bien mieux fonctionner que la réponse acceptée. Il ne supprime la dernière extension que si le fichier a plus d'un point, tandis que cut supprime tout après le premier point.
Dale Anderson


20

Si votre nom de fichier contient un point (autre que celui de l'extension), utilisez ceci:

echo $filename | rev | cut -f 2- -d '.' | rev

1
J'ai oublié le régime du milieu, mais une fois que je l'ai vu, c'était super!
suprême Pooba

C'est encore mieux avec une -soption donnée à cut, de sorte qu'elle renvoie une chaîne vide chaque fois que le nom de fichier ne contient pas de point.
Hibou57

1
Cela devrait être la réponse acceptée à mon avis, car cela fonctionne sur un chemin avec des points, avec des fichiers cachés commençant par un point, ou même avec un fichier avec plusieurs extensions.
Tim Krief le

20
file1=/tmp/main.one.two.sh
t=$(basename "$file1")                        # output is main.one.two.sh
name=$(echo "$file1" | sed -e 's/\.[^.]*$//') # output is /tmp/main.one.two
name=$(echo "$t" | sed -e 's/\.[^.]*$//')     # output is main.one.two

utilisez ce que vous voulez. Ici, je suppose que le dernier .(point) suivi du texte est l'extension.


6
#!/bin/bash
file=/tmp/foo.bar.gz
echo $file ${file%.*}

les sorties:

/tmp/foo.bar.gz /tmp/foo.bar

Notez que seule la dernière extension est supprimée.


3

Ma recommandation est d'utiliser basename.
C'est par défaut dans Ubuntu, un code visuellement simple et traiter la majorité des cas.

Voici quelques sous-cas pour traiter les espaces et les multi-points / sous-extensions:

pathfile="../space fld/space -file.tar.gz"
echo ${pathfile//+(*\/|.*)}

Il se débarrasse généralement de l'extension du premier ., mais échoue dans notre ..chemin

echo **"$(basename "${pathfile%.*}")"**  
space -file.tar     # I believe we needed exatly that

Voici une note importante:

J'ai utilisé des guillemets doubles à l'intérieur des guillemets doubles pour gérer les espaces. Le devis simple ne passera pas en raison de l'envoi du $. Bash est inhabituel et lit les «deuxièmes» premières «citations» en raison de l'expansion.

Cependant, vous devez toujours penser à .hidden_files

hidden="~/.bashrc"
echo "$(basename "${hidden%.*}")"  # will produce "~" !!!  

pas le résultat attendu. Pour y arriver, utilisez $HOMEou /home/user_path/
car encore une fois bash est "inhabituel" et ne développez pas "~" (recherchez bash BashPitfalls)

hidden2="$HOME/.bashrc" ;  echo '$(basename "${pathfile%.*}")'

1
#!/bin/bash
filename=program.c
name=$(basename "$filename" .c)
echo "$name"

les sorties:

program

En quoi est-ce différent de la réponse donnée par Steven Penny il y a 3 ans?
gniourf_gniourf

1

Comme le souligne Hawker65 dans le commentaire de la réponse de chepner, la solution la plus votée ne prend pas en charge les extensions multiples (comme filename.tar.gz), ni les points dans le reste du chemin (comme this.path / with .dots / in.path.name). Une solution possible est:

a=this.path/with.dots/in.path.name/filename.tar.gz
echo $(dirname $a)/$(basename $a | cut -d. -f1)

Celui-ci supprime "tar.gz" en sélectionnant des caractères avant la première instance d'un point dans le nom de fichier sans compter le chemin. On ne veut probablement pas supprimer les extensions de cette façon.
Frotz

0

Deux problèmes avec votre code:

  1. Vous avez utilisé un '(coche) au lieu d'un' (coche arrière) pour entourer les commandes qui génèrent la chaîne que vous souhaitez stocker dans la variable.
  2. Vous n'avez pas "renvoyé" la variable "$ filename" au tube dans la commande "cut".

Je changerais votre code en "name =` echo $ filename | cut -f 1 -d '.' `", comme indiqué ci-dessous (encore une fois, notez les coches arrière entourant la définition de la variable de nom):

$> filename=foo.txt
$> echo $filename
foo.txt
$> name=`echo $filename | cut -f1 -d'.'`
$> echo $name
foo
$> 
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.