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...

81voto

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

funktioniert gut, Sie können also 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 folgendermaßen.

Der Befehl für NAME ersetzt eine "." Zeichen, gefolgt von einer beliebigen Anzahl von Nicht "." Zeichen bis zum Ende der Zeile, mit nichts (d.h. es wird alles aus der letzten "." bis zum Ende der Zeile, einschließlich). Dies ist im Grunde eine nicht-greedy Substitution mit Regex-Tricks.

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

0 Stimmen

Dies gilt nicht für Dateien ohne Erweiterung, da für Name und Erweiterung dasselbe gedruckt würde. Ich verwende also sed 's,\.[^\.]*$,,' für Name, und sed 's,.*\.,., ;t ;g' für die Erweiterung (verwendet die atypische test y get Befehle, zusammen mit den typischen substitute Befehl).

0 Stimmen

Sie könnten nach der Berechnung von NAME prüfen, ob NAME und DATEI gleich sind, und wenn ja, EXTENSION auf eine leere Zeichenkette setzen.

0 Stimmen

Grundsätzlich ist die Verwendung eines externen Prozesses für etwas, das die Shell selbst erledigen kann, ein Antipattern.

51voto

Bjarke Freund-Hansen Punkte 25882

Sie können verwenden basename .

Exemple :

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

Sie müssen basename mit der Erweiterung angeben, die entfernt werden soll, aber wenn Sie immer tar con -z dann wissen Sie, dass die Erweiterung lautet .tar.gz .

Dies sollte das tun, was Sie wollen:

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

2 Stimmen

Ich nehme an cd $(basename $1 .tar.gz) funktioniert für .gz-Dateien. Aber in der Frage erwähnte er Archive files have several extensions: tar.gz, tat.xz, tar.bz2

0 Stimmen

Tomi Po hat 2 Jahre zuvor das Gleiche geschrieben.

0 Stimmen

Hallo Blauhirn, wauw das ist eine alte Frage. Ich glaube, mit den Daten ist etwas passiert. Ich erinnere mich deutlich daran, dass ich die Frage kurz nach ihrer Beantwortung beantwortet habe, und dass es nur ein paar andere Antworten gab. Könnte es sein, dass die Frage mit einer anderen zusammengelegt wurde, macht SO das?

45voto

Kebabbert Punkte 429

Schreibt Mellen in einem Kommentar zu einem Blogeintrag:

Mit der Bash gibt es auch ${file%.*} um den Dateinamen ohne die Erweiterung zu erhalten und ${file##*.} um die Verlängerung allein zu erhalten. Das heißt,

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

Ausgänge:

filename: thisfile
extension: txt

2 Stimmen

38voto

henfiber Punkte 1069

Hier sind einige alternative Vorschläge (meist 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 vollständigen Originalpfad als Eingabe, ohne auf Zwischenergebnisse angewiesen zu sein.

35voto

Cyker Punkte 8424

Sie brauchen sich nicht mit awk o sed oder sogar perl für diese einfache Aufgabe. Es gibt eine reine Bash, os.path.splitext() -kompatible Lösung, die nur Parametererweiterungen verwendet.

Referenz Implementierung

Dokumentation von os.path.splitext(path) :

Den Pfadnamen Pfad in ein Paar aufteilen (root, ext) tal que root + ext == path y ext leer ist oder mit einem Punkt beginnt und höchstens einen Punkt enthält. Führende Punkte im Basisnamen werden ignoriert; splitext('.cshrc') gibt zurück. ('.cshrc', '') .

Python-Code:

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

Bash-Implementierung

Ehrung von Spitzenzeiten

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

Führende Perioden ignorieren

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

Tests

Hier sind Testfälle für die Führende Perioden ignorieren Implementierung, die bei jeder Eingabe mit der Python-Referenzimplementierung übereinstimmen sollte.

|---------------|-----------|-------|
|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' |
|---------------|-----------|-------|

Test Ergebnisse

Alle Tests wurden bestanden.

4 Stimmen

Nein, der Basisdateiname für text.tar.gz sollte sein text und Erweiterung sein .tar.gz

3 Stimmen

@frederick99 Wie ich schon sagte, entspricht die Lösung hier der Implementierung von os.path.splitext in Python. Ob diese Implementierung für möglicherweise kontroverse Eingaben vernünftig ist, ist ein anderes Thema.

0 Stimmen

Wie werden die Anführungszeichen innerhalb des Musters ( "$root" ) funktionieren? Was könnte passieren, wenn sie weggelassen werden? (Ich konnte keine Dokumentation zu diesem Thema finden.) Wie werden außerdem Dateinamen mit * o ? in ihnen?

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