Abrufen der Erweiterung in einem Dateinamen

Wie erhalte ich die Dateierweiterung von bash? Folgendes habe ich versucht:

filename=`basename $filepath` fileext=${filename##*.} 

Auf diese Weise kann ich die Erweiterung von bz2 vom Pfad /dir/subdir/file.bz2, aber ich habe ein Problem mit dem Pfad /dir/subdir/file-1.0.tar.bz2.

Ich würde eine Lösung bevorzugen, die nur bash ohne externes verwendet Programme, wenn es möglich ist.

Um meine Frage klar zu stellen, habe ich ein Bash-Skript erstellt, um ein bestimmtes Archiv nur mit einem einzigen Befehl von extract path_to_file zu extrahieren Das Extrahieren der Datei wird vom Skript anhand des Komprimierungs- oder Archivierungstyps festgelegt, der .tar.gz, .gz, .bz2 usw. sein kann. Ich denke, dies sollte eine Zeichenfolgenmanipulation beinhalten, beispielsweise wenn ich die Erweiterung .gz dann sollte ich prüfen, ob die Zeichenfolge .tar vor .gz steht – wenn ja, sollte die Erweiterung .tar.gz.

Kommentare

  • file = “ /dir/subdir/file-1.0.tar.bz2

; echo $ {file ## *.} druckt hier ‚ .bz2 ‚. Was ist die Ausgabe, die Sie ‚ erwarten?

  • Ich benötige .tar.bz2
  • Related : Dateiname und Erweiterung in Bash extrahieren .
  • Antwort

    Wenn der Dateiname file-1.0.tar.bz2 lautet, lautet die Erweiterung bz2. Die Methode, mit der Sie die Erweiterung extrahieren (fileext=${filename##*.}), ist vollkommen gültig¹.

    Wie entscheiden Sie, dass die Erweiterung tar.bz2 und nicht bz2 oder 0.tar.bz2? Sie müssen diese Frage zuerst beantworten. Dann können Sie herausfinden, was Der Shell-Befehl entspricht Ihrer Spezifikation.

    • Eine mögliche Spezifikation ist, dass Erweiterungen mit einem Buchstaben beginnen müssen. Diese Heuristik schlägt für einige häufig verwendete Erweiterungen wie 7z, was am besten als Sonderfall behandelt werden kann. Hier „sa bash / ksh / zsh-Implementierung:

      basename=$filename; fileext= while [[ $basename = ?*.* && ( ${basename##*.} = [A-Za-z]* || ${basename##*.} = 7z ) ]] do fileext=${basename##*.}.$fileext basename=${basename%.*} done fileext=${fileext%.} 

      Für die POSIX-Portabilität müssen Sie verwenden eine case -Anweisung für den Mustervergleich.

      while case $basename in ?*.*) case ${basename##*.} in [A-Za-z]*|7z) true;; *) false;; esac;; *) false;; esac do … 
    • Eine andere mögliche Spezifikation ist, dass einige Erweiterungen bezeichnen Codierungen und zeigen an, dass weiteres Strippen erforderlich ist. Hier „sa bash / ksh / zsh-Implementierung (erfordert shopt -s extglob unter bash und setopt ksh_glob unter zsh):

      basename=$filename fileext= while [[ $basename = ?*.@(bz2|gz|lzma) ]]; do fileext=${basename##*.}.$fileext basename=${basename%.*} done if [[ $basename = ?*.* ]]; then fileext=${basename##*.}.$fileext basename=${basename%.*} fi fileext=${fileext%.} 

      Beachten Sie, dass dies 0 als Erweiterung in file-1.0.gz betrachtet.

    ¹ ${VARIABLE##SUFFIX} und verwandte Konstrukte befinden sich in POSIX Sie funktionieren in jeder nicht antiken Shell im Bourne-Stil wie Asche, Bash, Ksh oder Zsh.

    Kommentare

    • , die sollten gelöst werden, indem überprüft wird, ob die Zeichenfolge vor dem letzten . -Token vom Archivtyp ist, z. B. tar, wenn es sich nicht um einen Archivtyp wie 0 Die Iteration sollte enden.
    • @uray: Das funktioniert in diesem speziellen Fall, aber ‚ ist keine allgemeine Lösung Betrachten Sie das Beispiel von Maciej ‚ für .patch.lzma Es wäre wichtig, die Zeichenfolge nach dem letzten . zu betrachten: wenn sie ‚ ein Komprimierungssuffix (.7z, .bz2, .gz, …), Strippen Sie weiter.
    • @NoamM Was war falsch an der Einrückung? ‚ ist nach Ihrer Bearbeitung definitiv fehlerhaft: doppelt verschachtelter Code wird genauso eingerückt wie einfach verschachtelter.

    Antwort

    Sie können die Sache vereinfachen, indem Sie nur einen Mustervergleich für den Dateinamen durchführen, anstatt die Erweiterung zweimal zu extrahieren:

    case "$filename" in *.tar.bz2) bunzip_then_untar ;; *.bz2) bunzip_only ;; *.tar.gz) untar_with -z ;; *.tgz) untar_with -z ;; *.gz) gunzip_only ;; *.zip) unzip ;; *.7z) do something ;; *) do nothing ;; esac 

    Kommentare

    • Diese Lösung ist sehr einfach.

    Antwort

    $ echo "thisfile.txt"|awk -F . "{print $NF}" 

    Kommentare hierzu: http://liquidat.wordpress.com/2007/09/29/short-tip-get-file-extension-in-shell-script/

    Kommentare

    • funktioniert nicht für die .tar.gz -Erweiterung
    • Nun, ein .tar .gz ist eigentlich ein Teer in einer GZIP-Datei, funktioniert also in dem Sinne, dass eine GZ-Erweiterung aus einer GZIP-Datei entfernt wird.

    Antwort

    Hier ist mein Schuss: Übersetzen Sie Punkte in Zeilenumbrüche, leiten Sie durch tail, letzte Zeile abrufen:

    $> TEXT=123.234.345.456.456.567.678 $> echo $TEXT | tr . \\n | tail -n1 678 

    Antwort

    Eines Tages habe ich diese kniffligen Funktionen erstellt:

    # args: string how_many function get_last_letters(){ echo ${1:${#1}-$2:$2}; } function cut_last_letters(){ echo ${1:0:${#1}-$2}; } 

    Ich habe diesen einfachen Ansatz gefunden, der in vielen Fällen sehr nützlich ist, nicht nur, wenn er funktioniert Informationen zu Erweiterungen.

    Zum Überprüfen von Erweiterungen – Es ist einfach und zuverlässig.

    ~$ get_last_letters file.bz2 4 .bz2 ~$ get_last_letters file.0.tar.bz2 4 .bz2 

    Zum Abschneiden der Erweiterung:

    ~$ cut_last_letters file.0.tar.bz2 4 file.0.tar 

    Zum Ändern der Erweiterung:

    ~$ echo $(cut_last_letters file.0.tar.bz2 4).gz file.0.tar.gz 

    Oder, wenn Sie „praktische Funktionen“ mögen:

    ~$ function cut_last_letters_and_add(){ echo ${1:0:${#1}-$2}"$3"; } ~$ cut_last_letters_and_add file.0.tar.bz2 4 .gz file.0.tar.gz 

    PS Wenn Ihnen diese Funktionen gefallen haben oder Sie sie als nützlich empfunden haben, Bitte lesen Sie diesen Beitrag 🙂 (und geben Sie hoffentlich einen Kommentar ab).

    Antwort

    echo ${filename#$(echo $filename | sed "s/\.[^[:digit:]].*$//g;")} 

    Zum Beispiel:

    Kommentare

    • Funktioniert nicht in allen Fällen. Versuchen Sie es mit ‚ foo.7z ‚
    • Sie benötigen Anführungszeichen und verwenden besser printf falls der Dateiname einen Backslash enthält oder mit - beginnt: "${filename#$(printf %s "$filename" | sed 's/\.[^[:digit:]].*$//g;')}"
    • @axel_c : richtig, und ich ‚ habe die gleiche Spezifikation wie Maciej als Beispiel implementiert. Welche Heuristik schlagen Sie vor, dass ‚ besser ist als „beginnt mit einem Buchstaben“?
    • @Gilles: Ich denke nur, dass dort ‚ ist keine Lösung, es sei denn, Sie verwenden eine vorberechnete Liste bekannter Erweiterungen, da eine Erweiterung alles sein kann.

    Antwort

    Die auf Jackman-Groß- und Kleinschreibung basierende Antwort ist ziemlich gut und portabel. Wenn Sie jedoch nur den Dateinamen und die Erweiterung in einer Variablen haben möchten, habe ich diese Lösung gefunden:

    INPUTFILE="$1" INPUTFILEEXT=$( echo -n "$INPUTFILE" | rev | cut -d"." -f1 | rev ) INPUTFILEEXT=$( echo -n $INPUTFILEEXT | tr "[A-Z]" "[a-z]" ) # force lowercase extension INPUTFILENAME="`echo -n \"$INPUTFILE\" | rev | cut -d"." -f2- | rev`" # fix for files with multiple extensions like "gbamidi-v1.0.tar.gz" INPUTFILEEXT2=$( echo -n "$INPUTFILENAME" | rev | cut -d"." -f1 | rev ) if [ "$INPUTFILEEXT2" = "tar" ]; then # concatenate the extension INPUTFILEEXT="$INPUTFILEEXT2.$INPUTFILEEXT" # update the filename INPUTFILENAME="`echo -n \"$INPUTFILENAME\" | rev | cut -d"." -f2- | rev`" fi 

    Es funktioniert nur mit doppelten Erweiterungen und die erste muss „tar“ sein.

    Sie können die „tar“ -Testzeile jedoch durch einen Stringlängentest ändern und den Fix mehrmals wiederholen .

    Antwort

    Ich habe es folgendermaßen gelöst:

    filename=`basename $filepath` fileext=${filename##*.} fileext2=${filename%.*} fileext3=${fileext2##*.} if [ "$fileext3" == "tar" ]; then fileext="tar."$fileext fi 

    Dies funktioniert jedoch nur für bekannte Archivierungstypen, in diesem Fall nur tar

    Schreibe einen Kommentar

    Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.