web-dev-qa-db-fra.com

Saisir l'extension dans un nom de fichier

Comment puis-je obtenir l'extension de fichier de bash? Voici ce que j'ai essayé:

filename=`basename $filepath`
fileext=${filename##*.}

En faisant que je peux obtenir une extension de bz2 du chemin /dir/subdir/file.bz2, mais j'ai un problème avec le chemin /dir/subdir/file-1.0.tar.bz2.

Je préférerais une solution utilisant uniquement Bash sans programmes externes si cela est possible.

Pour mettre ma question claire, je créais un script Bash pour extraire une archive donnée juste par une seule commande de extract path_to_file. Comment extraire le fichier est déterminé par le script en voyant son type de compression ou d'archivage, qui pourrait être .tar.gz, .gz, .bz2, etc., je pense que cela devrait impliquer une manipulation de chaîne, par exemple si je reçois l'extension .gz Ensuite, je devrais vérifier si la chaîne .tar avant .gz - Si oui, l'extension devrait être .tar.gz.

36
uray

Si le nom du fichier est file-1.0.tar.bz2, l'extension est bz2. La méthode que vous utilisez pour extraire l'extension (fileext=${filename##*.}) est parfaitement valide¹.

Comment décidez-vous que vous voulez que l'extension soit tar.bz2 et pas bz2 ou 0.tar.bz2? Vous devez d'abord répondre à cette question. Ensuite, vous pouvez déterminer la commande shell correspond à votre spécification.

  • Une spécification possible est que les extensions doivent commencer par une lettre. Cette heuristique échoue à quelques extensions courantes comme 7z, qui pourrait être mieux traité comme un cas particulier. Voici une implémentation BASH/KSH/ZSH:

    basename=$filename; fileext=
    while [[ $basename = ?*.* &&
             ( ${basename##*.} = [A-Za-z]* || ${basename##*.} = 7z ) ]]
    do
      fileext=${basename##*.}.$fileext
      basename=${basename%.*}
    done
    fileext=${fileext%.}
    

    Pour la portabilité POSIX, vous devez utiliser une instruction case pour la correspondance des motifs.

    while case $basename in
            ?*.*) case ${basename##*.} in [A-Za-z]*|7z) true;; *) false;; esac;;
            *) false;;
          esac
    do …
    
  • Une autre spécification possible est que certaines extensions désignent les codages et indiquent que d'autres décapages sont nécessaires. Voici une implémentation BASH/KSH/ZSH (nécessitant shopt -s extglob sous Bash et setopt ksh_glob sous ZSH):

    basename=$filename
    fileext=
    while [[ $basename = ?*.@(bz2|gz|lzma) ]]; do
      fileext=${basename##*.}.$fileext
      basename=${basename%.*}
    done
    if [[ $basename = ?*.* ]]; then
      fileext=${basename##*.}.$fileext
      basename=${basename%.*}
    fi
    fileext=${fileext%.}
    

    Notez que cela considère 0 Pour être une extension dans file-1.0.gz.

¹ ${VARIABLE##SUFFIX} et les constructions associées sont dans POSIX , ils fonctionnent donc dans n'importe quelle coquille non antique de style Bourne telle que cendres, bash, ksh ou zsh.

Vous pouvez simplifier les choses en faisant correspondre la correspondance des modèles sur le nom de fichier plutôt que d'extraire le prolongement deux fois:

case "$filename" in
    *.tar.bz2) bunzip_then_untar ;;
    *.bz2)     bunzip_only ;;
    *.tar.gz)  untar_with -z ;;
    *.tgz)     untar_with -z ;;
    *.gz)      gunzip_only ;;
    *.Zip)     unzip ;;
    *.7z)      do something ;;
    *)         do nothing ;;
esac
25
glenn jackman
$ echo "thisfile.txt"|awk -F . '{print $NF}'

Commentaires à ce sujet ici: http://liquidat.wordpress.com/2007/09/29/short-tip-get-file-Eltension-in-shell-script/

7
Chris

Voici mon coup de photo: traduisez les points vers les nouvelles lignes, le tuyau via tail, obtenez la dernière ligne:

$> TEXT=123.234.345.456.456.567.678
$> echo $TEXT | tr . \\n | tail -n1
678
2
Michael Bar-Sinai
echo ${filename#$(echo $filename | sed 's/\.[^[:digit:]].*$//g;')}

Par exemple:

% echo $filename
2.6.35-zen2.patch.lzma
% echo ${filename#$(echo $filename | sed 's/\.[^[:digit:]].*$//g;')}
.patch.lzma
0
Maciej Piechotka

Un jour j'ai créé ces fonctions difficiles:

# args: string how_many
function get_last_letters(){ echo ${1:${#1}-$2:$2}; }
function cut_last_letters(){ echo ${1:0:${#1}-$2}; }

J'ai trouvé cette approche simple, très utile dans de nombreux cas, non seulement quand cela va à propos des extensions.

Pour vérifier les extensions c'est simple et fiable

~$ get_last_letters file.bz2 4
.bz2
~$ get_last_letters file.0.tar.bz2 4
.bz2

Pour une extension de découpe:

~$ cut_last_letters file.0.tar.bz2 4
file.0.tar

Pour changer l'extension:

~$ echo $(cut_last_letters file.0.tar.bz2 4).gz
file.0.tar.gz

Ou, si vous aimez "des fonctions pratiques:

~$ function cut_last_letters_and_add(){ echo ${1:0:${#1}-$2}"$3"; }
~$ cut_last_letters_and_add file.0.tar.bz2 4 .gz
file.0.tar.gz

P.s. Si vous avez aimé ces fonctions ou les trouva trouvées à votre approvisionnement, veuillez vous reporter à cet article :) (et espérons-le mettre un commentaire).

0

la réponse basée sur le caisse Jackman est plutôt bonne et portable, mais si vous voulez juste que le nom de fichier et l'extension dans une variable, j'ai trouvé cette solution:

INPUTFILE="$1"
INPUTFILEEXT=$( echo -n "$INPUTFILE" | rev | cut -d'.' -f1 | rev )
INPUTFILEEXT=$( echo -n $INPUTFILEEXT | tr '[A-Z]' '[a-z]' ) # force lowercase extension
INPUTFILENAME="`echo -n \"$INPUTFILE\" | rev | cut -d'.' -f2- | rev`"

# fix for files with multiple extensions like "gbamidi-v1.0.tar.gz"
INPUTFILEEXT2=$( echo -n "$INPUTFILENAME" | rev | cut -d'.' -f1 | rev )
if [ "$INPUTFILEEXT2" = "tar" ]; then
    # concatenate the extension
    INPUTFILEEXT="$INPUTFILEEXT2.$INPUTFILEEXT"
    # update the filename
    INPUTFILENAME="`echo -n \"$INPUTFILENAME\" | rev | cut -d'.' -f2- | rev`"
fi

Cela ne fonctionne que avec des extensions doubles et le premier doit être "goudron".

Mais vous pouvez modifier la ligne de test "goudron" avec un test de longueur de chaîne et répéter le correctif plusieurs fois.

0
eadmaster