Comment vérifier l'extension d'un nom de fichier dans un script bash?


170

J'écris un script de construction nocturne en bash.
Tout va bien et dandy sauf un petit hic:


#!/bin/bash

for file in "$PATH_TO_SOMEWHERE"; do
      if [ -d $file ]
      then
              # do something directory-ish
      else
              if [ "$file" == "*.txt" ]       #  this is the snag
              then
                     # do something txt-ish
              fi
      fi
done;

Mon problème est de déterminer l'extension du fichier et d'agir en conséquence. Je sais que le problème est dans l'instruction if, testant un fichier txt.

Comment puis-je déterminer si un fichier a un suffixe .txt?


Cela se cassera si vous avez un fichier avec un espace dans son nom.
jfg956

En plus de la réponse de Paul, vous pouvez utiliser $(dirname $PATH_TO_SOMEWHERE)et $(basename $PATH_TO_SOMEWHERE)diviser en dossier et répertoire et faire quelque chose de directory-ish et file-ish
McPeppr

Réponses:


250

Je pense que vous voulez dire "Les quatre derniers caractères de $ file sont-ils égaux à .txt?" Si tel est le cas, vous pouvez utiliser les éléments suivants:

if [ ${file: -4} == ".txt" ]

Notez que l'espace entre file:et -4est obligatoire, car le modificateur «: -» signifie quelque chose de différent.


1
à cette fin, vous pouvez également renommer command.com en command.txt sur une machine Windows.
hometoast le

9
Si vous souhaitez spécifier une inégalité, n'oubliez pas d'inclure des crochets supplémentaires: if [[$ {file: -4}! = ".Txt"]]
Ram Rajamony

4
@RamRajamony Pourquoi est-il nécessaire d'utiliser [[pour tester l'inégalité?
PesaLe

Je voulais souligner que l'espace après le côlon est important. ${var:-4}n'est pas le même que ${var: -4}; le premier (sans espace) se développera comme '-4' si var n'est pas défini le second (avec un espace) retourne les 4 derniers caractères de var.
pbatey

1
Dans bash, cela produira une erreur "[: ==: opérateur unaire attendu" sauf si vous mettez des guillemets autour de la première variable. Alors à la if [ "${file: -4}" == ".txt" ]place.
Giles B

264

Faire

if [ "$file" == "*.txt" ]

comme ça:

if [[ $file == *.txt ]]

Autrement dit, doubles crochets et pas de guillemets.

Le côté droit ==est un motif de coquille. Si vous avez besoin d'une expression régulière, utilisez =~then.


15
Je n'en savais rien. Il semble être un cas particulier que le côté droit de == ou! = Soit développé en tant que motif de coque. Personnellement, je pense que c'est plus clair que ma réponse.
Paul Stephenson

20
Je suis nouveau dans bash et il m'a fallu un peu de temps pour comprendre comment l'utiliser dans une ifinstruction conditionnelle multiple . Je le partage ici au cas où cela aiderait quelqu'un. if [[ ( $file == *.csv ) || ( $file == *.png ) ]]
joelostblom

6
@cheflo c'est bon pour plusieurs conditions en général. Dans ce cas précis, vous pouvez également utiliser if [[ $file =~ .*\.(csv|png) ]]. Il est plus court, plus clair, plus facile d'ajouter des extensions supplémentaires et pourrait être facilement configuré (en mettant "csv | png" dans une variable).
jox

4
Vous pouvez mettre des guillemets doubles autour du fichier. if [[ "$file" == *.txt ]]Si le fichier comporte des espaces dans son nom, des guillemets doubles sont requis.
shawnhcorey

Dois-je utiliser cette réponse au lieu de celle acceptée?
Freedo

26

Vous ne pouvez tout simplement pas être sûr sur un système Unix, qu'un fichier .txt est vraiment un fichier texte. Votre meilleur pari est d'utiliser "fichier". Essayez peut-être d'utiliser:

file -ib "$file"

Ensuite, vous pouvez utiliser une liste de types MIME pour comparer ou analyser la première partie du MIME où vous obtenez des éléments tels que "texte", "application", etc.


2
Comme file -i...inclut l'encodage mime, vous pouvez utiliserfile --mime-type -b ...
Wilf

24

Vous pouvez utiliser la commande "fichier" si vous souhaitez réellement obtenir des informations sur le fichier plutôt que de vous fier aux extensions.

Si vous vous sentez à l'aise avec l'utilisation de l'extension, vous pouvez utiliser grep pour voir si elle correspond.


oui je connais la filecommande. J'avais en fait essayé la correspondance basée sur la sortie de ladite commande ... mais j'échoue horriblement à ces instructions if.

17

Vous pouvez également faire:

   if [ "${FILE##*.}" = "txt" ]; then
       # operation for txt files here
   fi

11
case $FILE in *.txt ) ... ;; esacsemblerait plus robuste et idiomatique.
tripleee

11

Similaire à 'fichier', utilisez le 'mimetype -b' légèrement plus simple qui fonctionnera quelle que soit l'extension du fichier.

if [ $(mimetype -b "$MyFile") == "text/plain" ]
then
  echo "this is a text file"
fi

Edit: vous devrez peut-être installer libfile-mimeinfo-perl sur votre système si mimetype n'est pas disponible


1
Vous devez préciser que le script «mimetype» n'est pas disponible sur tous les systèmes.
gilbertpilz

Terminé, ajouté libfile-mimeinfo-perl
dargaud

5

La bonne réponse sur la façon de prendre l'extension disponible dans un nom de fichier sous Linux est:

${filename##*\.} 

Exemple d'impression de toutes les extensions de fichier dans un répertoire

for fname in $(find . -maxdepth 1 -type f) # only regular file in the current dir
    do  echo ${fname##*\.} #print extensions 
done

Votre réponse utilise une double barre oblique inverse, mais votre exemple n'utilise qu'une seule barre oblique inverse. Votre exemple est correct, votre réponse ne l'est pas.
gilbertpilz

3

J'ai écrit un script bash qui regarde le type d'un fichier puis le copie dans un emplacement, je l'utilise pour parcourir les vidéos que j'ai regardées en ligne à partir de mon cache Firefox:

#!/bin/bash
# flvcache script

CACHE=~/.mozilla/firefox/xxxxxxxx.default/Cache
OUTPUTDIR=~/Videos/flvs
MINFILESIZE=2M

for f in `find $CACHE -size +$MINFILESIZE`
do
    a=$(file $f | cut -f2 -d ' ')
    o=$(basename $f)
    if [ "$a" = "Macromedia" ]
        then
            cp "$f" "$OUTPUTDIR/$o"
    fi
done

nautilus  "$OUTPUTDIR"&

Il utilise des idées similaires à celles présentées ici, j'espère que cela sera utile à quelqu'un.


2

Je suppose que '$PATH_TO_SOMEWHERE'c'est quelque chose comme'<directory>/*' .

Dans ce cas, je changerais le code en:

find <directory> -maxdepth 1 -type d -exec ... \;
find <directory> -maxdepth 1 -type f -name "*.txt" -exec ... \;

Si vous voulez faire quelque chose de plus compliqué avec les noms de répertoire et de fichier texte, vous pouvez:

find <directory> -maxdepth 1 -type d | while read dir; do echo $dir; ...; done
find <directory> -maxdepth 1 -type f -name "*.txt" | while read txtfile; do echo $txtfile; ...; done

Si vous avez des espaces dans vos noms de fichiers, vous pouvez:

find <directory> -maxdepth 1 -type d | xargs ...
find <directory> -maxdepth 1 -type f -name "*.txt" | xargs ...

Ce sont d'excellents exemples de la façon dont vous faites des «boucles» dans shell. Les boucles explicites foret les whileboucles sont mieux réservées lorsque le corps de la boucle doit être plus complexe.
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.