bash: Wie geben Sie Dateierweiterungen zurück?

7

Ich möchte ein Skript schreiben, um nur die Erweiterung des Eingabedateinamens zurückzugeben. Zum Beispiel textfile.txt sollte zurückkehren txt.

Ich bin neu in Linux und Bash und versuche zu lernen, wie man grundlegende Skripte schreibt. Vielen Dank!

user138438
quelle
2
Was ist die linux-4.2.3.tar.xzDateierweiterung, die Sie von kernel.org herunterladen können ? Welche /bin/bashDateierweiterung ist auf Ihrem System mit Sicherheit verfügbar? Sie sehen, das Konzept der "Dateierweiterung" ist unter Linux ziemlich fremd. Der Teil nach dem Punkt hat für das System nur eine sehr geringe Bedeutung - er ist eher ein Hinweis für den Benutzer, was er von dieser Datei erwarten könnte. Und dieses Stichwort kann lügen oder falsch sein.
Mirek Długosz

Antworten:

25

Die Shell, zum Beispiel bash, verfügt über viele Funktionen zur Manipulation von Zeichenfolgen . Mit einer davon können Sie alles bis zu einem bestimmten Muster entfernen:

${VAR##GLOB}

Die obige Syntax entfernt alles aus der Variablen $VARbis zur ersten Übereinstimmung des Glob GLOB. Um die Dateierweiterung ohne Namen zu drucken, haben Sie folgende Möglichkeiten:

$ file="file.txt"
$ echo ${file##*.}
txt

Beachten Sie, dass dies auch mehr als eine "Erweiterung" betreffen kann:

$ file="file.new.txt"
$ echo ${file##*.}
txt

Wenn Sie das nicht möchten, verwenden Sie #stattdessen eine, wodurch die kürzeste Übereinstimmung anstelle der längsten entfernt wird:

$ echo ${file#*.}
new.txt

Um dies für alle Dateien in einem Verzeichnis auszuführen, können Sie Folgendes tun:

$ ls 
file.avi  file.pdf  file.png  file.txt
$ for file in *; do echo "$file : ${file##*.}"; done
file.avi : avi
file.pdf : pdf
file.png : png
file.txt : txt

Der Vollständigkeit halber können Sie den Dateinamen auch ohne Erweiterung abrufen, indem Sie ${file%.*}:

$ for file in *; do echo "$file : ${file##*.} : ${file%.*}"; done
file.avi : avi : file
file.pdf : pdf : file
file.png : png : file
file.txt : txt : file
terdon
quelle
Dies funktioniert nicht für Dinge wie foo / .git / bar / baz. Ich denke, Sie könnten echo test.txt | ausführen awk -F '/' '{print $ NF}' | awk -F '.' '{print $ NF}'
Paul M
@PaulM was meinst du? Das ist eine Datei ohne Erweiterung. Was würden Sie davon erwarten?
Terdon
echo "/foo/var/.git/foo/bar.bz" | awk -F '/' '{print $ NF}' | awk -F '.' '{print $ NF}' == >> bz
Paul M
@PaulM file="/foo/var/.git/foo/bar.bz"; echo "${file##*.}": bz. Ich sehe kein Problem.
Terdon
4

Mit Parametererweiterung :

$ x="textfile.txt"
$ echo ${x##*.}
txt

Das ${parameter##word}Muster entfernt das größte übereinstimmende Präfix word(alles außer dem Dateiende).

Chaos
quelle
4

Ein sehr einfaches Bash-Skript, das das tun könnte, was Sie wollen, wäre das Folgende:

#!/usr/bin/env bash

echo "$1" | awk -F '.' '{print $NF}'

Das Skript wird als aufgerufen script.sh test.txt, und was es tut, ist

  • es wird $1auf stdout gedruckt . $1ist in diesem Fall der erste Parameter, der dem Skript zugewiesen wird test.txt.

  • Die stdout-Ausgabe von echowird an stdin weitergeleitet, für awkdie der String ( test.txt) aufgeteilt wird, .und druckt dann das letzte Element des Split ( txtin diesem Fall).

Nur eine kleine Bemerkung, dies kann mit einem Einzeiler gemacht werden, weil es eine ziemlich einfache Aufgabe ist:

echo test.txt | awk -F '.' '{print $NF}'
Primero
quelle
1
Sie müssen Anführungszeichen hinzufügen $1, oder Sie riskieren, Dateinamen zu brechen, die Leerzeichen usw. enthalten.
AP
Dies funktioniert nicht für Dinge wie foo / .git / bar / baz. Ich denke, Sie könnten echo test.txt | ausführen awk -F '/' '{print $ NF}' | awk -F '.' '{print $ NF}'
Paul M
3
#!/bin/bash

my_file=`basename "$1"` # get full file name as a script  parameter and strip the path
my_extension="${my_file##*.}"
my_file="${my_file%.*}" # will return base file name before the extension

echo "$my_file"
echo "$my_extension"

Lauf:

./script.sh index.html

Ausgabe:

index
html

Weitere Informationen: Erweiterung der Shell-Parameter | gnu.org

AP
quelle
3

Zu diesem Zweck sind einige Einschränkungen zu beachten:

  • Die foo.d/barDatei hat keine Erweiterung, es ist keine fooDatei mit einer .d/barErweiterung.
  • Vielleicht möchten Sie auch berücksichtigen, dass dies .bashrckeine Erweiterung hat
  • Die .und ..spezielle Verzeichnisdateien sollten wahrscheinlich speziell behandelt werden und keine Erweiterung haben.
  • Wofür ist die Erweiterung file.tar.gz? gzoder tar.gz? Was ist mit holiday.picture.jpgoder bash-4.4?
  • Wofür solltest du zurückkehren foo.d/? Leer oder d?

Hier würde ich tun:

#! /bin/sh -
for file do
  case ${file##*/} in
    (?*.*) ext=${file##*.};;
    (*) ext=;;
  esac
  printf '%s\n' "$ext"
done
Stéphane Chazelas
quelle
Hervorragende Antworthandhabung auch die üblichen Vorbehalte und nützlichen Code vor allem als Funktion.
Ajaaskel
0

Ich werde einige weitere Optionen hinzufügen, falls Sie sedoder Perl installiert haben:

#!/bin/bash
ext=$(<<<"$1" sed -n 's/^[^.]*\.//p')
printf "Extension: $ext\n"
ext=$(<<<"$1" perl -ne 's/^.*?\.//&&print')
printf "Extension: $ext\n"
exit 0
  • s/^[^.]*\.//pin sedund s/^.*?\.//&&printin perl: Versuchen Sie, die kürzeste Folge von Zeichen zu ersetzen, auf die am Anfang der Zeichenfolge kein Zeichen .folgt .. Wenn die Ersetzung vorgenommen werden könnte, drucken Sie das Ergebnis der Ersetzung aus.
% bash script.sh file.bin
Extension: bin
Extension: bin
kos
quelle
0

Achtung. Unter Unix / Linux (und MacOS) ist die "Dateinamenerweiterung" nur informativ (wenn so viel). Sie können perfekt ein Perl-Skript photo.gifoder eine GIF-Datei mit dem Namen haben poem.txt. Oder sogar eine Datei namens some.txt.png.

vonbrand
quelle
Es ist in MS Windows genauso informativ, es ist nur so, dass das Standard-Shell-Programm dort stärkere Annahmen über die Paarung von Erweiterung und Inhalt macht, und sollte es eine Nichtübereinstimmung zwischen Benennung und Inhalt geben, fängt das Ganze normalerweise nur an, Fehler zu schreien, die Armen Ding.
nperson325681