2839 Stimmen

Dateiname und Erweiterung in Bash extrahieren

Ich möchte den Dateinamen (ohne Erweiterung) und die Erweiterung separat abrufen.

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

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

Dies ist falsch, weil es nicht funktioniert, wenn der Dateiname mehrere . Zeichen. Wenn, sagen wir mal, ich habe a.b.js wird sie folgende Punkte berücksichtigen a y b.js anstelle von a.b y js .

Dies kann in Python leicht mit

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

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

Haben Sie eine bessere Idee?

0 Stimmen

Diese Frage erklärt diese Bash-Technik und einige andere verwandte Techniken.

44 Stimmen

Wenn Sie die großartigen Antworten unten anwenden, fügen Sie nicht einfach Ihre Variable ein, wie ich es hier zeige Falsch: extension="{$filename##*.}" wie ich es eine Zeit lang getan habe! Bewegen Sie die $ außerhalb der Curlys: Richtig: extension="${filename##*.}"

4 Stimmen

Dies ist eindeutig ein nicht triviales Problem, und für mich ist es schwer zu sagen, ob die nachstehenden Antworten völlig korrekt sind. Es ist erstaunlich, dass dies kein eingebauter Vorgang in (ba)sh ist (die Antworten scheinen die Funktion mittels Mustervergleich zu implementieren). Ich beschloss, Pythons os.path.splitext stattdessen wie oben...

4437voto

Petesh Punkte 86615

Ermitteln Sie zunächst den Dateinamen ohne Pfadangabe:

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

Alternativ können Sie sich auf das letzte "/" des Pfades konzentrieren, was auch bei unvorhersehbaren Dateierweiterungen funktionieren sollte:

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

Schauen Sie in der Dokumentation nach:

94 Stimmen

Überprüfen Sie gnu.org/software/bash/manual/html_node/ für den vollen Funktionsumfang.

27 Stimmen

Fügen Sie einige Anführungszeichen zu "$fullfile" hinzu, sonst riskieren Sie, den Dateinamen zu zerstören.

53 Stimmen

Man könnte sogar filename="${fullfile##*/}" schreiben und so den Aufruf einer zusätzlichen basename

1155voto

Juliano Punkte 35709
~% FILE="example.tar.gz"

~% echo "${FILE%%.*}"
example

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

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

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

Für weitere Einzelheiten siehe Schalenparameter-Erweiterung im Bash-Handbuch.

40 Stimmen

Sie werfen (vielleicht unbeabsichtigt) die ausgezeichnete Frage auf, was zu tun ist, wenn der "Erweiterungsteil" des Dateinamens 2 Punkte enthält, wie in .tar.gz... Ich habe dieses Problem nie in Betracht gezogen, und ich vermute, es ist nicht lösbar, ohne alle möglichen gültigen Dateierweiterungen im Voraus zu kennen.

11 Stimmen

Warum nicht lösbar? In meinem Beispiel sollte berücksichtigt werden, dass die Datei Folgendes enthält zwei Erweiterungen, nicht eine Erweiterung mit zwei Punkten. Sie behandeln beide Erweiterungen getrennt.

32 Stimmen

Auf lexikalischer Basis ist das Problem nicht lösbar, Sie müssen den Dateityp überprüfen. Nehmen wir an, Sie hätten ein Spiel namens dinosaurs.in.tar und Sie gzipped es zu dinosaurs.in.tar.gz :)

631voto

Tomi Po Punkte 5579

In der Regel kennen Sie die Erweiterung bereits, so dass Sie sie verwenden können:

basename filename .extension

zum Beispiel:

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

und wir erhalten

filename

100 Stimmen

Dieses zweite Argument gegen basename ist ein ziemlicher Augenöffner, danke, mein Herr/Frau :)

14 Stimmen

Und wie extrahiert man die Erweiterung mit dieser Technik? ;) Oh, warte! Wir wissen es tatsächlich nicht im Voraus.

5 Stimmen

Angenommen, Sie haben ein gezipptes Verzeichnis, das entweder mit .zip o .ZIP . Gibt es eine Möglichkeit, wie Sie etwas tun können basename $file {.zip,.ZIP} ?

219voto

sotapme Punkte 4427

Sie können die Magie der POSIX-Parametererweiterung nutzen:

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

Es gibt eine Einschränkung: Wenn Ihr Dateiname die Form ./somefile.tar.gz dann echo ${FILENAME%%.*} würde gierig die längste Übereinstimmung mit der . und Sie hätten die leere Zeichenkette.

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

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

)


Diese Website 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

5 Stimmen

Viel einfacher als Joachims Antwort, aber ich muss immer die POSIX-Variablensubstitution nachschlagen. Außerdem läuft dies auf Max OSX, wo cut hat nicht --complement y sed hat keine -r .

94voto

Doctor J Punkte 5488

Das scheint nicht zu funktionieren, wenn die Datei keine Erweiterung oder keinen Dateinamen hat. Hier ist, was ich verwende; es verwendet nur builtins und behandelt mehr (aber nicht alle) pathologischen 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-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  = ""

3 Stimmen

Anstelle von dir="${fullpath:0:${#fullpath} - ${#filename}}" Ich habe oft gesehen dir="${fullpath%$filename}" . Es ist einfacher zu schreiben. Ich bin nicht sicher, ob es einen wirklichen Geschwindigkeitsunterschied oder Probleme gibt.

2 Stimmen

Dies verwendet #!/bin/bash, was fast immer falsch ist. Bevorzugen Sie #!/bin/sh wenn möglich oder #!/usr/bin/env bash wenn nicht.

0 Stimmen

@Guter Mensch: Ich weiß nicht, warum es fast immer falsch ist: which bash -> /bin/bash ; vielleicht liegt es an Ihrer Distro?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X