Extraire le nom de fichier et l'extension à 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'à présent est:

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

ceci est erroné parce que cela ne fonctionne pas si le nom du fichier contient plusieurs caractères . . 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 allumer un interpréteur Python juste pour ça, si possible.

de meilleures idées?

1672
demandé sur Benjamin W. 2009-06-08 18:00:29

30 réponses

tout d'abord, obtenir 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 '.'qui devrait fonctionner même si vous avez des extensions de fichier imprévisibles:

filename="${fullfile##*/}"
2875
répondu Petesh 2018-04-30 13:58:25
~% FILE="example.tar.gz"
~% echo "${FILE%%.*}"
example
~% echo "${FILE%.*}"
example.tar
~% echo "${FILE#*.}"
tar.gz
~% echo "${FILE##*.}"
gz

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

511
répondu Juliano 2015-04-20 16:58:03

habituellement, vous connaissez déjà l'extension, donc vous pourriez vouloir utiliser:

basename filename .extension

par exemple:

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

et nous obtenons

filename
288
répondu Tomi Po 2011-10-19 10:56:09

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 dans le fait que si votre nom de fichier était de la forme ./somefile.tar.gz alors echo ${FILENAME%%.*} enlèverait greedily la plus longue correspondance au . 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
répondu sotapme 2013-02-05 15:40:05

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 n'utilise que des builtins et gère plus (mais pas tous) de 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 testcases:

$ basename-and-extension.sh / /home/me/ /home/me/file /home/me/file.tar /home/me/file.tar.gz /home/me/.hidden /home/me/.hidden.tar /home/me/.. .
/:
    dir  = "/"
    base = ""
    ext  = ""
/home/me/:
    dir  = "/home/me/"
    base = ""
    ext  = ""
/home/me/file:
    dir  = "/home/me/"
    base = "file"
    ext  = ""
/home/me/file.tar:
    dir  = "/home/me/"
    base = "file"
    ext  = "tar"
/home/me/file.tar.gz:
    dir  = "/home/me/"
    base = "file.tar"
    ext  = "gz"
/home/me/.hidden:
    dir  = "/home/me/"
    base = ".hidden"
    ext  = ""
/home/me/.hidden.tar:
    dir  = "/home/me/"
    base = ".hidden"
    ext  = "tar"
/home/me/..:
    dir  = "/home/me/"
    base = ".."
    ext  = ""
.:
    dir  = ""
    base = "."
    ext  = ""
65
répondu Doctor J 2009-09-10 06:13:08

vous pouvez utiliser basename .

exemple:

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

Vous devez fournir les basename avec l'extension qui doit être supprimé, cependant, si vous êtes toujours d'exécution tar avec -z alors, vous savez, l'extension sera .tar.gz .

Ce qui devrait faire ce que vous voulez:

tar -zxvf 
cd $(basename  .tar.gz)
41
répondu Bjarke Freund-Hansen 2013-02-05 08:56:05
pax> echo a.b.js | sed 's/\.[^.]*$//'
a.b
pax> echo a.b.js | sed 's/^.*\.//'
js

fonctionne très bien, donc vous pouvez juste 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, soit dit en passant, fonctionnent comme suit.

la commande pour NAME remplace un caractère "." suivi de n'importe quel nombre de caractères non - "." jusqu'à la fin de la ligne, sans rien (i.e., il enlève tout du final "." jusqu'à la fin de la ligne, inclus). Il s'agit essentiellement d'une substitution non cupide utilisant la ruse regex.

la commande pour EXTENSION remplace un certain nombre de caractères suivi d'un "." caractère au début de la ligne, sans rien (c.-à-d., Il supprime tout du début de la ligne au point final, inclus). C'est une substitution cupide qui est l'action par défaut.

26
répondu paxdiablo 2017-09-05 01:07:22

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

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

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

sorties:

filename: thisfile
extension: txt
24
répondu Kebabbert 2015-04-20 16:58:54

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

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

comme L'a souligné Clayton Hughes dans un commentaire, cela ne fonctionnera pas pour l'exemple réel dans la question. Ainsi, comme 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

il fonctionne en supprimant les deux derniers (alpha-numérique) extensions sans condition.

[mise à jour après le commentaire D'Anders Lindahl]

22
répondu Some programmer dude 2013-12-04 08:08:34

Pas besoin de s'embêter avec awk ou sed ou même perl pour cette simple tâche. Il existe une solution os.path.splitext() compatible pur-Bash qui n'utilise que des extensions de paramètres.

Mise En Œuvre De Référence

Documentation de os.path.splitext(path) :

divise le chemin de chemin en une paire (root, ext) telle que root + ext == path , et ext est vide ou commence par une période et contient au plus une période. Les périodes de référence sur le basename sont ignorées; splitext('.cshrc') retourne ('.cshrc', '') .

code Python:

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

Bash Mise En Œuvre

Honorer le leader des périodes de

root="${path%.*}"
ext="${path#"$root"}"

en Ignorant les périodes de pointe

root="${path#.}";root="${path%"$root"}${root%.*}"
ext="${path#"$root"}"

Essais

Voici cas d'essai pour l'implémentation Ignoring leading periods , qui devrait correspondre à L'implémentation de référence Python pour chaque entrée.

|---------------|-----------|-------|
|path           |root       |ext    |
|---------------|-----------|-------|
|' .txt'        |' '        |'.txt' |
|' .txt.txt'    |' .txt'    |'.txt' |
|' txt'         |' txt'     |''     |
|'*.txt.txt'    |'*.txt'    |'.txt' |
|'.cshrc'       |'.cshrc'   |''     |
|'.txt'         |'.txt'     |''     |
|'?.txt.txt'    |'?.txt'    |'.txt' |
|'\n.txt.txt'   |'\n.txt'   |'.txt' |
|'\t.txt.txt'   |'\t.txt'   |'.txt' |
|'a b.txt.txt'  |'a b.txt'  |'.txt' |
|'a*b.txt.txt'  |'a*b.txt'  |'.txt' |
|'a?b.txt.txt'  |'a?b.txt'  |'.txt' |
|'a\nb.txt.txt' |'a\nb.txt' |'.txt' |
|'a\tb.txt.txt' |'a\tb.txt' |'.txt' |
|'txt'          |'txt'      |''     |
|'txt.pdf'      |'txt'      |'.pdf' |
|'txt.tar.gz'   |'txt.tar'  |'.gz'  |
|'txt.txt'      |'txt'      |'.txt' |
|---------------|-----------|-------|

Résultats Des Essais

tous les essais sont réussis.

22
répondu Cyker 2016-12-19 23:35:04

voici quelques suggestions alternatives (la plupart dans awk ), y compris certains cas d'utilisation avancée, comme l'extraction des numéros de version pour les paquets de logiciels.

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(/[^.]*[.]/, "", "151900920")} 1'

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

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

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

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

# Folder (containing the file) : 'complex'
    echo "$f" | awk -F'/' '{=""; 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'[/.]' '{=""; print "151900920"}'

# 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 original comme entrée, sans dépendre des résultats intermédiaires.

20
répondu henfiber 2015-06-16 09:02:52

[révisé d'une doublure unique à une bash Générique fonction , comportement maintenant compatible avec dirname et basename utilitaires; justification ajoutée.]

Le accepté de répondre fonctionne bien typique cas , mais échoue dans edge cas , à savoir:

  • pour les noms de fichiers sans l'extension (appelé suffixe dans le reste de cette réponse), extension=${filename##*.} renvoie le fichier d'entrée plutôt qu'une chaîne vide.
  • extension=${filename##*.} ne comprend pas le . initial , contrairement à la convention.
    • préparation Aveugle . ne fonctionnerait pas pour les noms de fichiers sans suffixe.
  • filename="${filename%.*}" sera une chaîne vide si le fichier d'entrée nom commence par . et ne contient pas d'autres . caractères (par exemple, .bash_profile ) - contrairement à la convention.

---------

ainsi, la complexité d'une solution robuste qui couvre toutes les arêtes cas appelle à une fonction - voir sa définition ci-dessous; il peut retourner tout composants d'un chemin .

exemple d'appel:

splitPath '/etc/bash.bashrc' dir fname fnameroot suffix
# -> $dir == '/etc'
# -> $fname == 'bash.bashrc'
# -> $fnameroot == 'bash'
# -> $suffix == '.bashrc'

notez que les arguments après le chemin d'entrée sont librement choisis, variable de position noms .

Pour sauter les variables qui ne sont pas d'intérêt et qui précèdent celles qui le sont, spécifiez _ (pour utiliser la variable de rejet $_ ) ou '' ; par exemple, pour extraire la racine du nom de fichier et l'extension seulement, utilisez splitPath '/etc/bash.bashrc' _ _ fnameroot extension .


# SYNOPSIS
#   splitPath path varDirname [varBasename [varBasenameRoot [varSuffix]]] 
# DESCRIPTION
#   Splits the specified input path into its components and returns them by assigning
#   them to variables with the specified *names*.
#   Specify '' or throw-away variable _ to skip earlier variables, if necessary.
#   The filename suffix, if any, always starts with '.' - only the *last*
#   '.'-prefixed token is reported as the suffix.
#   As with `dirname`, varDirname will report '.' (current dir) for input paths
#   that are mere filenames, and '/' for the root dir.
#   As with `dirname` and `basename`, a trailing '/' in the input path is ignored.
#   A '.' as the very first char. of a filename is NOT considered the beginning
#   of a filename suffix.
# EXAMPLE
#   splitPath '/home/jdoe/readme.txt' parentpath fname fnameroot suffix
#   echo "$parentpath" # -> '/home/jdoe'
#   echo "$fname" # -> 'readme.txt'
#   echo "$fnameroot" # -> 'readme'
#   echo "$suffix" # -> '.txt'
#   ---
#   splitPath '/home/jdoe/readme.txt' _ _ fnameroot
#   echo "$fnameroot" # -> 'readme'  
splitPath() {
  local _sp_dirname= _sp_basename= _sp_basename_root= _sp_suffix=
    # simple argument validation
  (( $# >= 2 )) || { echo "$FUNCNAME: ERROR: Specify an input path and at least 1 output variable name." >&2; exit 2; }
    # extract dirname (parent path) and basename (filename)
  _sp_dirname=$(dirname "")
  _sp_basename=$(basename "")
    # determine suffix, if any
  _sp_suffix=$([[ $_sp_basename = *.* ]] && printf %s ".${_sp_basename##*.}" || printf '')
    # determine basename root (filemane w/o suffix)
  if [[ "$_sp_basename" == "$_sp_suffix" ]]; then # does filename start with '.'?
      _sp_basename_root=$_sp_basename
      _sp_suffix=''
  else # strip suffix from filename
    _sp_basename_root=${_sp_basename%$_sp_suffix}
  fi
  # assign to output vars.
  [[ -n  ]] && printf -v "" "$_sp_dirname"
  [[ -n  ]] && printf -v "" "$_sp_basename"
  [[ -n  ]] && printf -v "" "$_sp_basename_root"
  [[ -n  ]] && printf -v "" "$_sp_suffix"
  return 0
}

test_paths=(
  '/etc/bash.bashrc'
  '/usr/bin/grep'
  '/Users/jdoe/.bash_profile'
  '/Library/Application Support/'
  'readme.new.txt'
)

for p in "${test_paths[@]}"; do
  echo ----- "$p"
  parentpath= fname= fnameroot= suffix=
  splitPath "$p" parentpath fname fnameroot suffix
  for n in parentpath fname fnameroot suffix; do
    echo "$n=${!n}"
  done
done

code D'essai qui exerce la fonction:

test_paths=(
  '/etc/bash.bashrc'
  '/usr/bin/grep'
  '/Users/jdoe/.bash_profile'
  '/Library/Application Support/'
  'readme.new.txt'
)

for p in "${test_paths[@]}"; do
  echo ----- "$p"
  parentpath= fname= fnameroot= suffix=
  splitPath "$p" parentpath fname fnameroot suffix
  for n in parentpath fname fnameroot suffix; do
    echo "$n=${!n}"
  done
done

résultats escomptés-noter les cas de bord:

  • un nom de fichier ayant pas de suffixe
  • un nom de fichier commençant par . ( pas considéré comme le début du suffixe)
  • un chemin d'entrée se terminant par / ("derrière / est ignoré)
  • un chemin d'entrée qui est un nom de fichier ( . est retourné en tant que parent chemin)
  • un nom de fichier qui a plus de . précédé d'un jeton (seul le dernier est considéré comme le suffixe):
----- /etc/bash.bashrc
parentpath=/etc
fname=bash.bashrc
fnameroot=bash
suffix=.bashrc
----- /usr/bin/grep
parentpath=/usr/bin
fname=grep
fnameroot=grep
suffix=
----- /Users/jdoe/.bash_profile
parentpath=/Users/jdoe
fname=.bash_profile
fnameroot=.bash_profile
suffix=
----- /Library/Application Support/
parentpath=/Library
fname=Application Support
fnameroot=Application Support
suffix=
----- readme.new.txt
parentpath=.
fname=readme.new.txt
fnameroot=readme.new
suffix=.txt
12
répondu mklement0 2017-05-23 12:10:43

je pense que si vous avez juste besoin du nom du fichier, vous pouvez essayer ceci:

FULLPATH=/usr/share/X11/xorg.conf.d/50-synaptics.conf

# Remove all the prefix until the "/" character
FILENAME=${FULLPATH##*/}

# Remove all the prefix until the "." character
FILEEXTENSION=${FILENAME##*.}

# Remove a suffix, in our case, the filename. This will return the name of the directory that contains this file.
BASEDIRECTORY=${FULLPATH%$FILENAME}

echo "path = $FULLPATH"
echo "file name = $FILENAME"
echo "file extension = $FILEEXTENSION"
echo "base directory = $BASEDIRECTORY"

et c'est tout =D.

12
répondu Andrew Woolfgang 2015-04-20 17:00:14

la solution la plus petite et la plus simple (en une seule ligne) est:

$ file=/blaabla/bla/blah/foo.txt

echo $(basename ${file%.*})

foo

12
répondu Ron 2017-04-22 14:39:16

vous pouvez forcer cut à afficher tous les champs et les suivants en ajoutant - au numéro de champ.

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

donc si le fichier est eth0.pcap.gz , L'EXTENSION sera pcap.gz

en utilisant la même logique, vous pouvez également récupérer le nom du fichier en utilisant '-' avec cut comme suit:

NAME=`basename "$FILE" | cut -d'.' -f-1`

Cela fonctionne même pour les noms de fichiers qui n'ont pas d'extension.

9
répondu maciek gajewski 2015-07-30 16:23:11

Ok donc si je comprends bien, le problème ici est de savoir comment obtenir le nom et l'extension complète d'un fichier qui a plusieurs extensions, par exemple, stuff.tar.gz .

ça marche pour moi:

fullfile="stuff.tar.gz"
fileExt=${fullfile#*.}
fileName=${fullfile%*.$fileExt}

cela vous donnera stuff comme nom de fichier et .tar.gz comme extension. Il fonctionne pour n'importe quel nombre d'extensions, y compris 0. Espérons que cela aide pour toute personne ayant le même problème =)

7
répondu Al3xXx 2016-12-02 10:26:40

la Magie de reconnaissance de fichiers

en plus des nombreuses bonnes réponses sur cette question de débordement de pile, j'aimerais ajouter:

sous Linux et autres unixen , il y a une magic commande nommée file , qui font la détection de type de fichier en analysant certains premiers octets du fichier. C'est un très vieil outil, initialement utilisé pour les serveurs d'impression (si ce n'est créée... Je ne suis pas sûr à ce sujet).

file myfile.txt
myfile.txt: UTF-8 Unicode text

file -b --mime-type myfile.txt
text/plain

les extensions Standards peuvent être trouvées dans /etc/mime.types (sur mon Debian GNU/Linux desktop. Voir man file et man mime.types . Peut-être que vous devez installer l'utilitaire file et les paquets mime-support ):

grep $( file -b --mime-type myfile.txt ) </etc/mime.types
text/plain      asc txt text pot brf srt

vous pouvez créer une fonction pour déterminer l'extension droite. Il y a un petit (pas parfait) échantillon:

file2ext() {
    local _mimetype=$(file -Lb --mime-type "") _line _basemimetype
    case ${_mimetype##*[/.-]} in
        gzip | bzip2 | xz | z )
            _mimetype=${_mimetype##*[/.-]}
            _mimetype=${_mimetype//ip}
            _basemimetype=$(file -zLb --mime-type "")
            ;;
        stream )
            _mimetype=($(file -Lb ""))
            [ "${_mimetype[1]}" = "compressed" ] &&
                _basemimetype=$(file -b --mime-type - < <(
                        ${_mimetype,,} -d <"")) ||
                _basemimetype=${_mimetype,,}
            _mimetype=${_mimetype,,}
            ;;
        executable )  _mimetype='' _basemimetype='' ;;
        dosexec )     _mimetype='' _basemimetype='exe' ;;
        shellscript ) _mimetype='' _basemimetype='sh' ;;
        * )
            _basemimetype=$_mimetype
            _mimetype=''
            ;;
    esac
    while read -a _line ;do
        if [ "$_line" == "$_basemimetype" ] ;then
            [ "$_line[1]" ] &&
                _basemimetype=${_line[1]} ||
                _basemimetype=${_basemimetype##*[/.-]}
            break
        fi
        done </etc/mime.types
    case ${_basemimetype##*[/.-]} in
        executable ) _basemimetype='' ;;
        shellscript ) _basemimetype='sh' ;;
        dosexec ) _basemimetype='exe' ;;
        * ) ;;
    esac
    [ "$_mimetype" ] && [ "$_basemimetype" != "$_mimetype" ] &&
      printf ${2+-v}  "%s.%s" ${_basemimetype##*[/.-]} ${_mimetype##*[/.-]} ||
      printf ${2+-v}  "%s" ${_basemimetype##*[/.-]}
}

cette fonction pourrait définir Variable Bash qui peut être utilisée plus tard:

(c'est inspiré de la réponse à droite de @Petesh):

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

echo "$fullfile -> $filename . $extension"
6
répondu F. Hauri 2015-04-20 17:04:11
$ F = "text file.test.txt"  
$ echo ${F/*./}  
txt  

cela s'applique à plusieurs points et Espaces Dans un nom de fichier, mais s'il n'y a pas d'extension, il renvoie le nom du fichier lui-même. Facile à vérifier cependant; juste tester pour le nom de fichier et l'extension étant la même.

naturellement, cette méthode ne fonctionne pas .tar.les fichiers gz. Toutefois, qui pourraient être traitées dans un processus en deux étapes. Si l'extension est gz, vérifiez à nouveau s'il y a aussi une extension tar.

4
répondu Miriam English 2011-04-10 13:58:28

j'utilise le script suivant

$ echo "foo.tar.gz"|rev|cut -d"." -f3-|rev
foo
4
répondu Joydip Datta 2014-03-22 12:56:10

Comment extraire le nom de fichier et l'extension en poisson :

function split-filename-extension --description "Prints the filename and extension"
  for file in $argv
    if test -f $file
      set --local extension (echo $file | awk -F. '{print $NF}')
      set --local filename (basename $file .$extension)
      echo "$filename $extension"
    else
      echo "$file is not a valid file"
    end
  end
end

mises en garde: fentes sur le dernier point, qui fonctionne bien pour les noms de fichiers avec des points en eux, mais pas bien pour les extensions avec des points en eux. Voir l'exemple ci-dessous.

Utilisation:

$ split-filename-extension foo-0.4.2.zip bar.tar.gz
foo-0.4.2 zip  # Looks good!
bar.tar gz  # Careful, you probably want .tar.gz as the extension.

Il y a probablement de meilleures façons de le faire. N'hésitez pas à modifier mon réponse de l'améliorer.


S'il y a un nombre limité d'extensions avec lesquelles vous aurez affaire et que vous connaissez toutes, essayez ceci:

switch $file
  case *.tar
    echo (basename $file .tar) tar
  case *.tar.bz2
    echo (basename $file .tar.bz2) tar.bz2
  case *.tar.gz
    echo (basename $file .tar.gz) tar.gz
  # and so on
end

Ce n' pas ont de la mise en garde que le premier exemple, mais vous n'avez pas à gérer tous les cas de sorte qu'il pourrait être plus ennuyeux en fonction du nombre d'extensions que vous pouvez vous attendre.

4
répondu Dennis 2014-03-30 21:41:03

voici le code avec AWK . Cela peut être fait plus simplement. Mais je ne suis pas bon à AWK.

filename$ ls
abc.a.txt  a.b.c.txt  pp-kk.txt
filename$ find . -type f | awk -F/ '{print }' | rev | awk -F"." '{="";print}' | rev | awk 'gsub(" ",".") ,sub(".$", "")'
abc.a
a.b.c
pp-kk
filename$ find . -type f | awk -F/ '{print }' | awk -F"." '{print $NF}'
txt
txt
txt
3
répondu smilyface 2015-04-20 17:00:58

une réponse simple:

pour développer sur le variables POSIX réponse , notez que vous pouvez faire des modèles plus intéressants. Donc pour le cas détaillé ici, vous pouvez simplement faire ceci:

tar -zxvf 
cd ${1%.tar.*}

qui coupera la dernière occurrence de .tar. 1519120920" .

plus généralement, si vous voulez supprimer la dernière occurrence de . . puis

${1.*.*}

devrait marcher.

Le lien de la réponse ci-dessus semble être mort. voici une excellente explication d'un tas de manipulations de cordes que vous pouvez faire directement dans Bash, à partir de TLDP .

2
répondu sqjoatmos 2015-04-20 17:07:49

bâtiment de Petesh réponse, si seulement le nom du fichier est nécessaire, le chemin et l'extension peuvent être dénudés en une seule ligne,

filename=$(basename ${fullname%.*})
2
répondu cvr 2017-05-23 12:34:45

utiliser simplement ${parameter%word}

dans votre cas:

${FILE%.*}

si vous voulez le tester, tous les travaux suivants, et il suffit de supprimer l'extension:

FILE=abc.xyz; echo ${FILE%.*};
FILE=123.abc.xyz; echo ${FILE%.*};
FILE=abc; echo ${FILE%.*};
2
répondu enyo 2018-05-14 15:48:43

basé en grande partie sur l'excellente réponse de @mklement0, et la pile-pleine de hasard, utile bashismes - ainsi que d'autres réponses à cette / d'autres questions / "que diable internet"... Je l'ai enveloppé dans un peu, un peu plus compréhensible, réutilisable fonction pour mon (ou votre) .bash_profile qui prend soin de ce qui (je considère) devrait être une version plus robuste de dirname / basename / qu'avez-vous ..

function path { SAVEIFS=$IFS; IFS=""   # stash IFS for safe-keeping, etc.
    [[ $# != 2 ]] && echo "usage: path <path> <dir|name|fullname|ext>" && return    # demand 2 arguments
    [[  =~ ^(.*/)?(.+)?$ ]] && {     # regex parse the path
        dir=${BASH_REMATCH[1]}
        file=${BASH_REMATCH[2]}
        ext=$([[ $file = *.* ]] && printf %s ${file##*.} || printf '')
        # edge cases for extesionless files and files like ".nesh_profile.coffee"
        [[ $file == $ext ]] && fnr=$file && ext='' || fnr=${file:0:$((${#file}-${#ext}))}
        case "" in
             dir) echo      "${dir%/*}"; ;;
            name) echo      "${fnr%.*}"; ;;
        fullname) echo "${fnr%.*}.$ext"; ;;
             ext) echo           "$ext"; ;;
        esac
    }
    IFS=$SAVEIFS
}     

exemples d'utilisation...

SOMEPATH=/path/to.some/.random\ file.gzip
path $SOMEPATH dir        # /path/to.some
path $SOMEPATH name       # .random file
path $SOMEPATH ext        # gzip
path $SOMEPATH fullname   # .random file.gzip                     
path gobbledygook         # usage: -bash <path> <dir|name|fullname|ext>
1
répondu Alex Gray 2013-08-27 02:18:45

des réponses ci-dessus, le plus court oneliner pour imiter Python

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

présumant que votre fichier a réellement une extension, est

EXT="${PATH##*.}"; FILE=$(basename "$PATH" .$EXT)
1
répondu commonpike 2015-04-20 17:04:50

si vous voulez aussi autoriser empty extensions, c'est la plus courte que je puisse trouver:

echo 'hello.txt' | sed -r 's/.+\.(.+)|.*//' # EXTENSION
echo 'hello.txt' | sed -r 's/(.+)\..+|(.*)//' # FILENAME

1ère ligne expliquée: elle correspond au chemin.EXT or ANYTHING and replaces it with EXT. S'il y a correspondance, le groupe ext n'est pas capturé.

1
répondu Blauhirn 2018-01-17 16:47:03

afin de rendre dir plus utile (dans le cas où un fichier local sans chemin est spécifié comme entrée) j'ai fait ce qui suit:

# Substring from 0 thru pos of filename
dir="${fullpath:0:${#fullpath} - ${#filename}}"
if [[ -z "$dir" ]]; then
    dir="./"
fi

Cela permet de faire quelque chose d'utile comme ajouter un suffixe au fichier d'entrée basename:

outfile=${dir}${base}_suffix.${ext}

testcase: foo.bar
dir: "./"
base: "foo"
ext: "bar"
outfile: "./foo_suffix.bar"

testcase: /home/me/foo.bar
dir: "/home/me/"
base: "foo"
ext: "bar"
outfile: "/home/me/foo_suffix.bar"
0
répondu Bill Gale 2013-08-09 00:05:19

vous pouvez utiliser

sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-

pour obtenir le nom de fichier et

sed 's/^/./' | rev | cut -d. -f1  | rev

pour obtenir une extension.

cas de Test:

echo "filename.gz"     | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename.gz"     | sed 's/^/./' | rev | cut -d. -f1  | rev
echo "filename"        | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename"        | sed 's/^/./' | rev | cut -d. -f1  | rev
echo "filename.tar.gz" | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename.tar.gz" | sed 's/^/./' | rev | cut -d. -f1  | rev
0
répondu Vytenis Bivainis 2014-05-22 20:24:16

Voici l'algorithme que j'ai utilisé pour trouver le nom et l'extension d'un fichier lorsque j'ai écrit un script Bash pour rendre les noms uniques lorsque les noms entraient en conflit par rapport au casing.

#! /bin/bash 

#
# Finds 
# -- name and extension pairs
# -- null extension when there isn't an extension.
# -- Finds name of a hidden file without an extension
# 

declare -a fileNames=(
  '.Montreal' 
  '.Rome.txt' 
  'Loundon.txt' 
  'Paris' 
  'San Diego.txt'
  'San Francisco' 
  )

echo "Script "151900920" finding name and extension pairs."
echo 

for theFileName in "${fileNames[@]}"
do
     echo "theFileName=${theFileName}"  

     # Get the proposed name by chopping off the extension
     name="${theFileName%.*}"

     # get extension.  Set to null when there isn't an extension
     # Thanks to mklement0 in a comment above.
     extension=$([[ "$theFileName" == *.* ]] && echo ".${theFileName##*.}" || echo '')

     # a hidden file without extenson?
     if [ "${theFileName}" = "${extension}" ] ; then
         # hidden file without extension.  Fixup.
         name=${theFileName}
         extension=""
     fi

     echo "  name=${name}"
     echo "  extension=${extension}"
done 

l'essai.

$ config/Name\&Extension.bash 
Script config/Name&Extension.bash finding name and extension pairs.

theFileName=.Montreal
  name=.Montreal
  extension=
theFileName=.Rome.txt
  name=.Rome
  extension=.txt
theFileName=Loundon.txt
  name=Loundon
  extension=.txt
theFileName=Paris
  name=Paris
  extension=
theFileName=San Diego.txt
  name=San Diego
  extension=.txt
theFileName=San Francisco
  name=San Francisco
  extension=
$ 

FYI: le programme complet de translittération et plus de cas d'essai peuvent être trouvés ici: https://www.dropbox.com/s/4c6m0f2e28a1vxf/avoid-clashes-code.zip?dl=0

0
répondu historystamp 2014-10-21 23:25:33