Question Extraire le nom de fichier et l'extension dans Bash


Je veux obtenir le nom de fichier (sans extension) et l'extension séparément.

La meilleure solution que j'ai trouvée jusqu'ici est:

NAME=`echo "$FILE" | cut -d'.' -f1`
EXTENSION=`echo "$FILE" | cut -d'.' -f2`

C'est faux car cela ne fonctionne pas si le nom du fichier contient plusieurs . personnages. Si, disons, j'ai a.b.js, il considérera a et b.js, au lieu de a.b et js.

Il peut être facilement fait en Python avec

file, ext = os.path.splitext(path)

mais je préférerais ne pas lancer un interprète Python juste pour cela, si possible.

De meilleures idées?


1614
2018-06-08 14:00


origine


Réponses:


Tout d'abord, obtenez le nom du fichier sans le chemin:

filename=$(basename -- "$fullfile")
extension="${filename##*.}"
filename="${filename%.*}"

Alternativement, vous pouvez vous concentrer sur le dernier '/' du chemin au lieu du '.' ce qui devrait fonctionner même si vous avez des extensions de fichiers imprévisibles:

filename="${fullfile##*/}"

2806
2018-06-08 14:05



~% FILE="example.tar.gz"
~% echo "${FILE%%.*}"
example
~% echo "${FILE%.*}"
example.tar
~% echo "${FILE#*.}"
tar.gz
~% echo "${FILE##*.}"
gz

Pour plus de détails, voir expansion de paramètre shell dans le manuel de Bash.


502
2018-06-08 14:05



Habituellement, vous connaissez déjà l'extension, vous pouvez donc utiliser:

basename filename .extension

par exemple:

basename /path/to/dir/filename.txt .txt

et nous obtenons

filename

279
2017-10-19 10:56



Vous pouvez utiliser la magie des variables POSIX:

bash-3.2$ FILENAME=somefile.tar.gz
bash-3.2$ echo ${FILENAME%%.*}
somefile
bash-3.2$ echo ${FILENAME%.*}
somefile.tar

Il y a une mise en garde à ce sujet si votre nom de fichier était de la forme ./somefile.tar.gz puis echo ${FILENAME%%.*} serait goulument enlever le plus long match à la . et vous auriez la chaîne vide.

(Vous pouvez contourner cela avec une variable temporaire:

FULL_FILENAME=$FILENAME
FILENAME=${FULL_FILENAME##*/}
echo ${FILENAME%%.*}

)


Ce site explique plus.

${variable%pattern}
  Trim the shortest match from the end
${variable##pattern}
  Trim the longest match from the beginning
${variable%%pattern}
  Trim the longest match from the end
${variable#pattern}
  Trim the shortest match from the beginning

125
2018-02-05 09:09



Cela ne semble pas fonctionner si le fichier n'a pas d'extension, ou pas de nom de fichier. Voici ce que j'utilise; il utilise seulement des builtins et gère plus (mais pas tous) les noms de fichiers pathologiques.

#!/bin/bash
for fullpath in "$@"
do
    filename="${fullpath##*/}"                      # Strip longest match of */ from start
    dir="${fullpath:0:${#fullpath} - ${#filename}}" # Substring from 0 thru pos of filename
    base="${filename%.[^.]*}"                       # Strip shortest match of . plus at least one non-dot char from end
    ext="${filename:${#base} + 1}"                  # Substring from len of base thru end
    if [[ -z "$base" && -n "$ext" ]]; then          # If we have an extension and no base, it's really the base
        base=".$ext"
        ext=""
    fi

    echo -e "$fullpath:\n\tdir  = \"$dir\"\n\tbase = \"$base\"\n\text  = \"$ext\""
done

Et voici quelques cas de test:

$ basename-and-extension.sh / / home / moi / / home / moi / fichier /home/me/file.tar /home/me/file.tar.gz /home/me/.hidden / home / moi / .hidden.tar / home / moi / ...
/:
    dir = "/"
    base = ""
    ext = ""
/ home / moi /:
    dir = "/ home / moi /"
    base = ""
    ext = ""
/ home / moi / fichier:
    dir = "/ home / moi /"
    base = "fichier"
    ext = ""
/home/me/file.tar:
    dir = "/ home / moi /"
    base = "fichier"
    ext = "tar"
/home/me/file.tar.gz:
    dir = "/ home / moi /"
    base = "fichier.tar"
    ext = "gz"
/home/me/.hidden:
    dir = "/ home / moi /"
    base = ".hidden"
    ext = ""
/home/me/.hidden.tar:
    dir = "/ home / moi /"
    base = ".hidden"
    ext = "tar"
/ home / moi / ..:
    dir = "/ home / moi /"
    base = ".."
    ext = ""
.:
    dir = ""
    base = "."
    ext = ""

65
2017-09-10 05:17



Vous pouvez utiliser basename.

Exemple:

$ basename foo-bar.tar.gz .tar.gz
foo-bar

Vous devez fournir le nom de base avec l'extension qui doit être supprimée, mais si vous exécutez toujours tar avec -z alors vous savez que l'extension sera .tar.gz.

Cela devrait faire ce que vous voulez:

tar -zxvf $1
cd $(basename $1 .tar.gz)

40
2018-02-05 08:50



Mellen écrit dans un commentaire sur un article de blog:

En utilisant Bash, il y a aussi ${file%.*} pour obtenir le nom de fichier sans l'extension et ${file##*.} pour obtenir l'extension seul. C'est,

file="thisfile.txt"
echo "filename: ${file%.*}"
echo "extension: ${file##*.}"

Les sorties:

filename: thisfile
extension: txt

24
2017-07-21 10:24



pax> echo a.b.js | sed 's/\.[^.]*$//'
a.b
pax> echo a.b.js | sed 's/^.*\.//'
js

fonctionne très bien, donc vous pouvez simplement utiliser:

pax> FILE=a.b.js
pax> NAME=$(echo "$FILE" | sed 's/\.[^.]*$//')
pax> EXTENSION=$(echo "$FILE" | sed 's/^.*\.//')
pax> echo $NAME
a.b
pax> echo $EXTENSION
js

Les commandes, en passant, fonctionnent comme suit.

La commande pour NAME substitue un "." caractère suivi par un nombre quelconque de non"." caractères jusqu'à la fin de la ligne, avec rien (c'est-à-dire, il enlève tout de la finale "."jusqu'à la fin de la ligne, inclusivement). C'est fondamentalement une substitution non-gourmande utilisant la ruse de regex.

La commande pour EXTENSION substitue un nombre quelconque de caractères suivi d'un "." caractère au début de la ligne, avec rien (c'est-à-dire, il supprime tout du début de la ligne au point final, inclusivement). C'est une substitution gourmande qui est l'action par défaut.


24
2018-06-08 14:14



Vous pouvez utiliser le cut commande pour supprimer les deux dernières extensions (le ".tar.gz" partie):

$ echo "foo.tar.gz" | cut -d'.' --complement -f2-
foo

Comme l'a noté Clayton Hughes dans un commentaire, cela ne fonctionnera pas pour l'exemple réel dans la question. Donc, comme une alternative, je propose d'utiliser sed avec des expressions régulières étendues, comme ceci:

$ echo "mpc-1.0.1.tar.gz" | sed -r 's/\.[[:alnum:]]+\.[[:alnum:]]+$//'
mpc-1.0.1

Cela fonctionne en supprimant inconditionnellement les deux dernières extensions (alphanumériques).

[Mis à jour à nouveau après le commentaire de Anders Lindahl]


22
2018-02-05 08:53



Voici quelques suggestions alternatives (principalement en awk), y compris certains cas d'utilisation avancés, comme l'extraction de numéros de version pour des progiciels.

f='/path/to/complex/file.1.0.1.tar.gz'

# Filename : 'file.1.0.x.tar.gz'
    echo "$f" | awk -F'/' '{print $NF}'

# Extension (last): 'gz'
    echo "$f" | awk -F'[.]' '{print $NF}'

# Extension (all) : '1.0.1.tar.gz'
    echo "$f" | awk '{sub(/[^.]*[.]/, "", $0)} 1'

# Extension (last-2): 'tar.gz'
    echo "$f" | awk -F'[.]' '{print $(NF-1)"."$NF}'

# Basename : 'file'
    echo "$f" | awk '{gsub(/.*[/]|[.].*/, "", $0)} 1'

# Basename-extended : 'file.1.0.1.tar'
    echo "$f" | awk '{gsub(/.*[/]|[.]{1}[^.]+$/, "", $0)} 1'

# Path : '/path/to/complex/'
    echo "$f" | awk '{match($0, /.*[/]/, a); print a[0]}'
    # or 
    echo "$f" | grep -Eo '.*[/]'

# Folder (containing the file) : 'complex'
    echo "$f" | awk -F'/' '{$1=""; print $(NF-1)}'

# Version : '1.0.1'
    # Defined as 'number.number' or 'number.number.number'
    echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?'

    # Version - major : '1'
    echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f1

    # Version - minor : '0'
    echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f2

    # Version - patch : '1'
    echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f3

# All Components : "path to complex file 1 0 1 tar gz"
    echo "$f" | awk -F'[/.]' '{$1=""; print $0}'

# Is absolute : True (exit-code : 0)
    # Return true if it is an absolute path (starting with '/' or '~/'
    echo "$f" | grep -q '^[/]\|^~/'

Tous les cas d'utilisation utilisent le chemin complet d'origine en entrée, sans dépendre des résultats intermédiaires.


19
2018-06-16 09:02