Frage Extrahiere Dateinamen und Erweiterung in Bash


Ich möchte den Dateinamen (ohne Erweiterung) und die Erweiterung getrennt erhalten.

Die beste Lösung, die ich bisher gefunden habe, ist:

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

Das ist falsch, weil es nicht funktioniert, wenn der Dateiname mehrere enthält . Figuren. Wenn, sagen wir, ich habe a.b.js, wird es in Betracht ziehen a und b.js, Anstatt von a.b und js.

Es kann leicht in Python mit gemacht werden

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

aber ich würde es vorziehen, keinen Python-Interpreter nur dafür zu starten, wenn möglich.

Irgendwelche besseren Ideen?


1614
2018-06-08 14:00


Ursprung


Antworten:


Nennen Sie zuerst den Dateinamen ohne den Pfad:

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

Alternativ können Sie sich auf das letzte '/' des Pfads anstelle des '.' was auch dann funktionieren sollte, wenn Sie unvorhersehbare Dateierweiterungen haben:

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

Für weitere Details, siehe Shell-Parameter-Erweiterung im Bash-Handbuch.


502
2018-06-08 14:05



Normalerweise kennen Sie die Erweiterung bereits. Sie können also Folgendes verwenden:

basename filename .extension

beispielsweise:

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

und wir bekommen

filename

279
2017-10-19 10:56



Sie können die Magie von POSIX-Variablen verwenden:

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

Es gibt einen Vorbehalt, wenn Ihr Dateiname das Formular enthält ./somefile.tar.gz dann echo ${FILENAME%%.*} würde gierig das längste Spiel entfernen . und du hättest die leere Zeichenfolge.

(Sie können das mit einer temporären Variable umgehen:

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

)


Dies Seite? ˅ erklärt mehr.

${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



Das scheint nicht zu funktionieren, wenn die Datei keine Erweiterung oder keinen Dateinamen hat. Hier ist, was ich benutze; Es verwendet nur eingebaute und behandelt mehr (aber nicht alle) pathologische Dateinamen.

#!/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

Und hier sind einige Testfälle:

$ basename-und-extension.sh / / home / me / / home / me / datei / home / me / file.tar / home / me / file.tar.gz / home / me / .hidden / home / me / .hidden.tar / home / me / ..
/:
    dir = "/"
    Basis = ""
    ext = ""
/ home / ich /:
    dir = "/ home / ich /"
    Basis = ""
    ext = ""
/ home / ich / Datei:
    dir = "/ home / ich /"
    base = "Datei"
    ext = ""
/home/me/file.tar:
    dir = "/ home / ich /"
    base = "Datei"
    ext = "Teer"
/home/me/file.tar.gz:
    dir = "/ home / ich /"
    base = "Datei.tar"
    ext = "gz"
/home/me/.hidden:
    dir = "/ home / ich /"
    base = ".hidden"
    ext = ""
/home/me/.hidden.tar:
    dir = "/ home / ich /"
    base = ".hidden"
    ext = "Teer"
/ home / ich / ..:
    dir = "/ home / ich /"
    Basis = ".."
    ext = ""
.:
    dir = ""
    Basis = "."
    ext = ""

65
2017-09-10 05:17



Sie können verwenden basename.

Beispiel:

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

Sie müssen den Basisnamen mit der Erweiterung angeben, die entfernt werden soll, wenn Sie jedoch immer ausgeführt werden tar mit -z dann weißt du, dass die Erweiterung sein wird .tar.gz.

Dies sollte tun, was Sie wollen:

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

40
2018-02-05 08:50



Mellen schreibt in einem Kommentar zu einem Blogbeitrag:

Mit Bash gibt es auch ${file%.*} um den Dateinamen ohne die Erweiterung und zu erhalten ${file##*.} um die Erweiterung alleine zu bekommen. Das ist,

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

Ausgänge:

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

funktioniert gut, so können Sie einfach verwenden:

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

Die Befehle funktionieren übrigens wie folgt.

Der Befehl für NAME ersetzt a "." Charakter gefolgt von einer beliebigen Anzahl von nicht"." Zeichen bis zum Ende der Zeile, mit nichts (d. h. es entfernt alles aus dem Finale "."bis zum Ende der Zeile, inklusive). Dies ist im Grunde eine nicht-gierige Ersetzung mit Regex Tricks.

Der Befehl für EXTENSION ersetzt eine beliebige Anzahl von Zeichen, gefolgt von a "." Zeichen am Anfang der Zeile, mit nichts (d. h. es entfernt alles vom Anfang der Zeile bis zum letzten Punkt einschließlich). Dies ist eine gierige Ersetzung, die die Standardaktion ist.


24
2018-06-08 14:14



Du könntest das benutzen cut Befehl zum Entfernen der letzten beiden Erweiterungen (der ".tar.gz" Teil):

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

Wie von Clayton Hughes in einem Kommentar angemerkt, wird dies für das aktuelle Beispiel in der Frage nicht funktionieren. Als Alternative schlage ich vor, zu verwenden sed mit erweiterten regulären Ausdrücken, so:

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

Es funktioniert, indem die letzten zwei (alphanumerischen) Erweiterungen bedingungslos entfernt werden.

[Aktualisiert nach dem Kommentar von Anders Lindahl]


22
2018-02-05 08:53



Hier sind einige alternative Vorschläge (meistens in awk), einschließlich einiger fortgeschrittener Anwendungsfälle, wie das Extrahieren von Versionsnummern für Softwarepakete.

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 '^[/]\|^~/'

Alle Anwendungsfälle verwenden den ursprünglichen vollständigen Pfad als Eingabe, ohne von Zwischenergebnissen abhängig zu sein.


19
2018-06-16 09:02