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?

Anthon
la source
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.

Paul Stephenson
la source
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.


la source
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.

Eldelshell
la source
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.

Adam Peck
la source
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
kvz
la source
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

Dargaud
la source
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
Albin. Com.
la source
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.

décoder
la source
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 ...
jfg956
la source
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.