Extrahieren Sie Dateinamen und Erweiterung in Bash

2107

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

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

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

Dies ist falsch, da es nicht funktioniert, wenn der Dateiname mehrere .Zeichen enthält . Wenn, sagen wir, ich habe a.b.js, wird es aund b.jsanstelle von a.bund berücksichtigen js.

Es kann leicht in Python mit gemacht werden

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

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

Irgendwelche besseren Ideen?

ibz
quelle
Diese Frage erklärt diese Bash-Technik und einige andere verwandte.
jjclarkson
28
Wenn Sie die folgenden großartigen Antworten anwenden, fügen Sie Ihre Variable nicht einfach so ein, wie ich es hier zeige. Falsch: extension="{$filename##*.}" wie ich es eine Weile getan habe! Bewegen Sie die $Locken nach außen: Rechts: extension="${filename##*.}"
Chris K
4
Dies ist eindeutig ein nicht triviales Problem, und für mich ist es schwer zu sagen, ob die folgenden Antworten vollständig korrekt sind. Es ist erstaunlich, dass dies keine in (ba) sh integrierte Operation ist (Antworten scheinen die Funktion mithilfe des Mustervergleichs zu implementieren). Ich entschied mich os.path.splitextstattdessen für Pythons wie oben ...
Peter Gibson
1
Da die Erweiterung die Natur einer Datei darstellen muss, gibt es einen magischen Befehl, der die Datei überprüft, um seine Natur zu erraten und die Standarderweiterung anzubieten . siehe meine Antwort
F. Hauri
2
Die Frage ist in erster Linie problematisch, weil ... Aus Sicht des Betriebssystems und der Unix-Dateisysteme im Allgemeinen gibt es keine Dateierweiterung. Verwendung einer "." Teile zu trennen ist eine menschliche Konvention , die nur funktioniert, solange die Menschen damit einverstanden sind. Beispielsweise könnte mit dem Programm 'tar' entschieden worden sein, Ausgabedateien mit einem "tar" zu benennen. Präfix anstelle eines Suffixes ".tar" - Geben Sie "tar.somedir" anstelle von "somedir.tar" an. Aus diesem Grund gibt es keine "allgemeine, immer funktionierende" Lösung - Sie müssen Code schreiben, der Ihren spezifischen Anforderungen und erwarteten Dateinamen entspricht.
CM

Antworten:

3500

Holen Sie sich zuerst den Dateinamen ohne den Pfad:

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

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

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

Möglicherweise möchten Sie die Dokumentation überprüfen:

Petesh
quelle
85
Den vollständigen Funktionsumfang finden Sie unter gnu.org/software/bash/manual/html_node/… .
D.Shawley
24
Fügen Sie "$ fullfile" einige Anführungszeichen hinzu, da sonst der Dateiname beschädigt wird.
lhunath
47
Heck, Sie könnte sogar schreiben filename = "$ {Full - File - ## * /}" und vermeiden Sie fordern eine extrabasename
ephemient
45
Diese "Lösung" funktioniert nicht, wenn die Datei keine Erweiterung hat. Stattdessen wird der gesamte Dateiname ausgegeben, was ziemlich schlecht ist, wenn man bedenkt, dass Dateien ohne Erweiterungen allgegenwärtig sind.
nccc
43
Fix für den Umgang mit Dateinamen ohne Erweiterung : extension=$([[ "$filename" = *.* ]] && echo ".${filename##*.}" || echo ''). Beachten Sie, dass , wenn eine Erweiterung ist vorhanden, wird es mit dem anfänglichen zurückgeführt wird ., zum Beispiel .txt.
mklement0
684
~% FILE="example.tar.gz"

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

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

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

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

Weitere Informationen finden Sie unter Erweiterung der Shell-Parameter im Bash-Handbuch.

Juliano
quelle
22
Sie (vielleicht unbeabsichtigt) werfen die ausgezeichnete Frage auf, was zu tun ist, wenn der "Erweiterungsteil" des Dateinamens zwei Punkte enthält, wie in .tar.gz ... Ich habe dieses Problem nie in Betracht gezogen, und ich vermute, dass es das ist nicht lösbar, ohne alle möglichen gültigen Dateierweiterungen im Voraus zu kennen.
Rmeador
8
Warum nicht lösbar? In meinem Beispiel sollte berücksichtigt werden, dass die Datei zwei Erweiterungen enthält , keine Erweiterung mit zwei Punkten. Sie behandeln beide Erweiterungen getrennt.
Juliano
22
Es ist lexikalisch nicht lösbar. Sie müssen den Dateityp überprüfen. Überlegen Sie, ob Sie ein Spiel namens hatten dinosaurs.in.tarund es auf dinosaurs.in.tar.gz:)
gzippten
11
Dies wird komplizierter, wenn Sie auf vollen Pfaden fahren. Einer von mir hatte ein '.' in einem Verzeichnis in der Mitte des Pfades, aber keines im Dateinamen. Beispiel "a / bc / d / e / Dateiname" würde ".c / d / e / Dateiname" enden
Walt Sellers
6
eindeutig ist no x.tar.gz's Erweiterung gzund der Dateiname ist x.tardas ist es. Es gibt keine doppelten Erweiterungen. Ich bin mir ziemlich sicher, dass boost :: filesystem so damit umgeht. (geteilter Pfad, change_extension ...) und sein Verhalten basiert auf Python, wenn ich mich nicht irre.
v.oddou
430

Normalerweise kennen Sie die Erweiterung bereits, daher möchten Sie möglicherweise Folgendes verwenden:

basename filename .extension

zum Beispiel:

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

und wir bekommen

filename
Tomi Po
quelle
60
Das zweite Argument basenameist ziemlich der Augenöffner, meine Güte, Sir / Madam :)
akaIDIOT
10
Und wie extrahiere ich die Erweiterung mit dieser Technik? ;) Oh, Moment mal! Wir wissen es eigentlich nicht im Voraus.
Tomasz Gandor
3
Angenommen, Sie haben ein komprimiertes Verzeichnis, das entweder mit .zipoder endet .ZIP. Gibt es eine Möglichkeit, wie Sie so etwas tun könnten basename $file {.zip,.ZIP}?
Dennis
8
Dies beantwortet zwar nur einen Teil der OP-Frage, beantwortet jedoch die Frage, die ich in Google eingegeben habe. :-) Sehr schlau!
Sudo Make Install
1
einfach und POSIX-konform
gpanda
146

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 darin, dass wenn Ihr Dateiname von der Form wäre, ./somefile.tar.gzdann echo ${FILENAME%%.*}gierig die längste Übereinstimmung mit dem entfernt würde .und Sie die leere Zeichenfolge hätten.

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

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

)


Diese 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
sotapme
quelle
5
Viel einfacher als Joachims Antwort, aber ich muss immer nach der POSIX-Variablensubstitution suchen. Dies läuft auch unter Max OSX, wo cutes nicht gibt --complementund sednicht gibt -r.
Jwadsack
72

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

#!/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 = "Datei"
    ext = ""
/home/me/file.tar:
    dir = "/ home / me /"
    base = "Datei"
    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 = ""
Doktor J.
quelle
2
Statt dir="${fullpath:0:${#fullpath} - ${#filename}}"habe ich oft gesehen dir="${fullpath%$filename}". Es ist einfacher zu schreiben. Ich bin mir nicht sicher, ob es einen echten Geschwindigkeitsunterschied oder Fallstricke gibt.
Dubiousjim
2
Dies verwendet #! / Bin / bash, was fast immer falsch ist. Bevorzugen Sie wenn möglich #! / Bin / sh oder #! / Usr / bin / env bash, wenn nicht.
Gute Person
@ Gute Person: Ich weiß nicht, wie es fast immer falsch ist: which bash-> /bin/bash; Vielleicht ist es deine Distribution?
Vol7ron
2
@ vol7ron - in vielen Distributionen ist bash in / usr / local / bin / bash. Unter OSX installieren viele Benutzer eine aktualisierte Bash in / opt / local / bin / bash. Als solches ist / bin / bash falsch und man sollte env verwenden, um es zu finden. Noch besser ist es, / bin / sh- und POSIX-Konstrukte zu verwenden. Außer bei Solaris ist dies eine POSIX-Shell.
Gute Person
2
@ GoodPerson, aber wenn Sie mit Bash besser vertraut sind, warum sollten Sie sh verwenden? Ist das nicht so, als würde man sagen, warum Perl verwenden, wenn man sh verwenden kann?
Vol7ron
46

Sie können verwenden basename.

Beispiel:

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

Sie müssen Basisnamen mit der Erweiterung schaffen, der jedoch entfernt werden soll, wenn Sie immer die Ausführung tarmit -zdann wissen Sie , die Erweiterung sein wird .tar.gz.

Dies sollte tun, was Sie wollen:

tar -zxvf $1
cd $(basename $1 .tar.gz)
Bjarke Freund-Hansen
quelle
2
Ich nehme an, cd $(basename $1 .tar.gz)funktioniert für .gz-Dateien. Aber in Frage erwähnte erArchive files have several extensions: tar.gz, tat.xz, tar.bz2
SS Hegde
Tomi Po hat vor 2 Jahren die gleichen Dinge gepostet.
Phil294
Hallo Blauhirn, wauw das ist eine alte Frage. Ich denke, mit den Daten ist etwas passiert. Ich erinnere mich noch genau daran, wie ich die Frage kurz nach ihrer Beantwortung beantwortet habe, und dort gab es nur ein paar andere Antworten. Könnte es sein, dass die Frage mit einer anderen zusammengeführt wurde, macht SO das?
Bjarke Freund-Hansen
Ja, ich erinnere mich richtig. Ich beantworte diese Frage ursprünglich stackoverflow.com/questions/14703318/… am selben Tag, an dem sie gestellt wurde, 2 Jahre später wurde sie in diese zusammengeführt. Ich kann kaum für eine doppelte Antwort verantwortlich gemacht werden, wenn meine Antwort auf diese Weise verschoben wurde.
Bjarke Freund-Hansen
37
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 zum NAMEErsetzen eines "."Zeichens, gefolgt von einer beliebigen Anzahl von Nicht- "."Zeichen bis zum Ende der Zeile, mit nichts (dh er entfernt alles vom Finale "."bis zum Ende der Zeile, einschließlich). Dies ist im Grunde eine nicht gierige Substitution mit Regex-Tricks.

Der Befehl zum EXTENSIONErsetzen einer beliebigen Anzahl von Zeichen, gefolgt von einem "."Zeichen am Zeilenanfang, durch nichts (dh er entfernt alles vom Zeilenanfang bis einschließlich zum letzten Punkt). Dies ist eine gierige Ersetzung, die die Standardaktion ist.

paxdiablo
quelle
Diese Unterbrechung gilt für Dateien ohne Erweiterung, da sie für Name und Erweiterung gleich gedruckt wird. Also benutze ich sed 's,\.[^\.]*$,,'für Name und sed 's,.*\.,., ;t ;g'Erweiterung (verwendet die atypischen testund getBefehle zusammen mit dem typischen substituteBefehl).
HPPy
32

Mellen schreibt in einem Kommentar zu einem Blogbeitrag:

Mit Bash können Sie auch ${file%.*}den Dateinamen ohne die Erweiterung abrufen und ${file##*.}die Erweiterung alleine abrufen. Das ist,

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

Ausgänge:

filename: thisfile
extension: txt
Kebabbert
quelle
2
@ REACHUS: Siehe gnu.org/software/bash/manual/html_node/…
mklement0
29

Keine Notwendigkeit, sich mit awkoder sedoder sogar perlfür diese einfache Aufgabe zu beschäftigen. Es gibt eine reine Bash- os.path.splitext()kompatible Lösung, die nur Parametererweiterungen verwendet.

Referenzimplementierung

Dokumentation von os.path.splitext(path):

Teilen Sie den Pfad Weg in ein Paar , (root, ext)so dass root + ext == path, und ext leer ist oder beginnt mit einem Punkt und enthält höchstens eine Periode. Führende Punkte im Basisnamen werden ignoriert. splitext('.cshrc')kehrt zurück ('.cshrc', '').

Python-Code:

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

Bash-Implementierung

Führende Perioden ehren

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

Vorlaufzeiten ignorieren

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

Tests

Hier sind Testfälle für die Implementierung " Ignorieren führender Perioden" , die bei jeder Eingabe mit der Python-Referenzimplementierung übereinstimmen sollten.

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

Testergebnisse

Alle Tests bestanden.

Cyker
quelle
2
nein, der Basisdateiname für text.tar.gzsollte textund die Erweiterung sein.tar.gz
frederick99
2
@ frederick99 Wie gesagt, die Lösung hier entspricht der Implementierung os.path.splitextin Python. Ob diese Implementierung für möglicherweise kontroverse Eingaben sinnvoll ist, ist ein weiteres Thema.
Cyker
Wie funktionieren die Anführungszeichen in pattern ( "$root")? Was könnte passieren, wenn sie weggelassen würden? (Ich konnte keine Dokumentation zu diesem Thema finden.) Wie wird auch mit Dateinamen mit *oder ?in ihnen umgegangen ?
Ymett
Ok, Tests zeigen mir, dass die Anführungszeichen das Muster zu einem Literal machen, dh *und ?nichts Besonderes sind. Die beiden Teile meiner Frage beantworten sich also gegenseitig. Stimmt es, dass dies nicht dokumentiert ist? Oder soll dies aus der Tatsache verstanden werden, dass Zitate die Glob-Expansion im Allgemeinen deaktivieren?
Ymett
Geniale Antwort! Ich werde nur eine etwas einfachere Variante für die Berechnung des Stamms vorschlagen: root="${path#?}";root="${path::1}${root%.*}"- Fahren Sie dann mit dem gleichen Verfahren fort, um die Erweiterung zu extrahieren.
Maëlan
26

Mit dem cutBefehl können Sie die letzten beiden Erweiterungen (das ".tar.gz"Teil) entfernen :

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

Wie von Clayton Hughes in einem Kommentar bemerkt, funktioniert dies für das tatsächliche Beispiel in der Frage nicht. Als Alternative schlage ich vor, sedmit erweiterten regulären Ausdrücken wie folgt zu verwenden:

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

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

[Nach Kommentar von Anders Lindahl erneut aktualisiert]

Ein Programmierer
quelle
4
Dies funktioniert nur in dem Fall, in dem der Dateiname / Pfad keine anderen Punkte enthält: echo "mpc-1.0.1.tar.gz" | cut -d '.' --komplement -f2- erzeugt "mpc-1" (nur die ersten 2 Felder nach der Begrenzung durch.)
Clayton Hughes
@ClaytonHughes Du hast recht, und ich hätte es besser testen sollen. Eine weitere Lösung wurde hinzugefügt.
Einige Programmierer Typ
Die sed-Ausdrücke sollten verwenden $, um zu überprüfen, ob sich die übereinstimmende Erweiterung am Ende des Dateinamens befindet. Andernfalls kann ein Dateiname wie zu einem i.like.tar.gz.files.tar.bz2unerwarteten Ergebnis führen.
Anders Lindahl
@AndersLindahl Es wird immer noch, wenn die Reihenfolge der Erweiterungen die Umkehrung der sedKettenreihenfolge ist. Auch mit $am Ende ein Dateiname wie mpc-1.0.1.tar.bz2.tar.gzwird beide .tar.gzund dann entfernen .tar.bz2.
Einige Programmierer Typ
$ echo "foo.tar.gz" | cut -d '.' -f2- WITHOUT --complement bringt das 2. geteilte Element an das Ende der Zeichenfolge $ echo "foo.tar.gz" | cut -d '.' -f2- tar.gz
Gene Black
23

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.

Henfiber
quelle
20

Die akzeptierte Antwort funktioniert gut in typischen Fällen , aber nicht in Rand Fällen , nämlich:

  • Gibt bei Dateinamen ohne Erweiterung ( im Rest dieser Antwort als Suffix bezeichnet ) extension=${filename##*.}den Eingabedateinamen anstelle einer leeren Zeichenfolge zurück.
  • extension=${filename##*.}enthält nicht die Initiale ., entgegen der Konvention.
    • Das blinde Voranstellen .würde für Dateinamen ohne Suffix nicht funktionieren.
  • filename="${filename%.*}"wird die leere Zeichenfolge sein, wenn der Name der Eingabedatei mit beginnt .und keine weiteren .Zeichen enthält (z. B. .bash_profile) - entgegen der Konvention.

---------

Die Komplexität einer robusten Lösung, die alle Randfälle abdeckt, erfordert daher eine Funktion - siehe Definition unten; Es kann alle Komponenten eines Pfades zurückgeben .

Beispielaufruf:

splitPath '/etc/bash.bashrc' dir fname fnameroot suffix
# -> $dir == '/etc'
# -> $fname == 'bash.bashrc'
# -> $fnameroot == 'bash'
# -> $suffix == '.bashrc'

Man beachte , dass die Argumente , die nach dem Eingangsweg frei gewählt werden, Positionsvariablennamen .
Um Variablen zu überspringen, die nicht von Interesse sind und vor denen stehen, geben Sie Folgendes an _(um die Wegwerfvariable zu verwenden $_) oder ''; Verwenden Sie z splitPath '/etc/bash.bashrc' _ _ fnameroot extension. B. nur den Dateinamen root und die Erweiterung .


# SYNOPSIS
#   splitPath path varDirname [varBasename [varBasenameRoot [varSuffix]]] 
# DESCRIPTION
#   Splits the specified input path into its components and returns them by assigning
#   them to variables with the specified *names*.
#   Specify '' or throw-away variable _ to skip earlier variables, if necessary.
#   The filename suffix, if any, always starts with '.' - only the *last*
#   '.'-prefixed token is reported as the suffix.
#   As with `dirname`, varDirname will report '.' (current dir) for input paths
#   that are mere filenames, and '/' for the root dir.
#   As with `dirname` and `basename`, a trailing '/' in the input path is ignored.
#   A '.' as the very first char. of a filename is NOT considered the beginning
#   of a filename suffix.
# EXAMPLE
#   splitPath '/home/jdoe/readme.txt' parentpath fname fnameroot suffix
#   echo "$parentpath" # -> '/home/jdoe'
#   echo "$fname" # -> 'readme.txt'
#   echo "$fnameroot" # -> 'readme'
#   echo "$suffix" # -> '.txt'
#   ---
#   splitPath '/home/jdoe/readme.txt' _ _ fnameroot
#   echo "$fnameroot" # -> 'readme'  
splitPath() {
  local _sp_dirname= _sp_basename= _sp_basename_root= _sp_suffix=
    # simple argument validation
  (( $# >= 2 )) || { echo "$FUNCNAME: ERROR: Specify an input path and at least 1 output variable name." >&2; exit 2; }
    # extract dirname (parent path) and basename (filename)
  _sp_dirname=$(dirname "$1")
  _sp_basename=$(basename "$1")
    # determine suffix, if any
  _sp_suffix=$([[ $_sp_basename = *.* ]] && printf %s ".${_sp_basename##*.}" || printf '')
    # determine basename root (filemane w/o suffix)
  if [[ "$_sp_basename" == "$_sp_suffix" ]]; then # does filename start with '.'?
      _sp_basename_root=$_sp_basename
      _sp_suffix=''
  else # strip suffix from filename
    _sp_basename_root=${_sp_basename%$_sp_suffix}
  fi
  # assign to output vars.
  [[ -n $2 ]] && printf -v "$2" "$_sp_dirname"
  [[ -n $3 ]] && printf -v "$3" "$_sp_basename"
  [[ -n $4 ]] && printf -v "$4" "$_sp_basename_root"
  [[ -n $5 ]] && printf -v "$5" "$_sp_suffix"
  return 0
}

test_paths=(
  '/etc/bash.bashrc'
  '/usr/bin/grep'
  '/Users/jdoe/.bash_profile'
  '/Library/Application Support/'
  'readme.new.txt'
)

for p in "${test_paths[@]}"; do
  echo ----- "$p"
  parentpath= fname= fnameroot= suffix=
  splitPath "$p" parentpath fname fnameroot suffix
  for n in parentpath fname fnameroot suffix; do
    echo "$n=${!n}"
  done
done

Testcode, der die Funktion ausübt:

test_paths=(
  '/etc/bash.bashrc'
  '/usr/bin/grep'
  '/Users/jdoe/.bash_profile'
  '/Library/Application Support/'
  'readme.new.txt'
)

for p in "${test_paths[@]}"; do
  echo ----- "$p"
  parentpath= fname= fnameroot= suffix=
  splitPath "$p" parentpath fname fnameroot suffix
  for n in parentpath fname fnameroot suffix; do
    echo "$n=${!n}"
  done
done

Erwartete Ausgabe - beachten Sie die Randfälle:

  • Ein Dateiname ohne Suffix
  • ein Dateiname, der mit beginnt .(gilt nicht als Beginn des Suffixes)
  • ein Eingabepfad, der auf endet /(Trailing /wird ignoriert)
  • Ein Eingabepfad, der nur ein Dateiname ist ( .wird als übergeordneter Pfad zurückgegeben).
  • Ein Dateiname mit mehr als einem .vorfixierten Token (nur der letzte wird als Suffix betrachtet):
----- /etc/bash.bashrc
parentpath=/etc
fname=bash.bashrc
fnameroot=bash
suffix=.bashrc
----- /usr/bin/grep
parentpath=/usr/bin
fname=grep
fnameroot=grep
suffix=
----- /Users/jdoe/.bash_profile
parentpath=/Users/jdoe
fname=.bash_profile
fnameroot=.bash_profile
suffix=
----- /Library/Application Support/
parentpath=/Library
fname=Application Support
fnameroot=Application Support
suffix=
----- readme.new.txt
parentpath=.
fname=readme.new.txt
fnameroot=readme.new
suffix=.txt
mklement0
quelle
19

Die kleinste und einfachste Lösung (in einer Zeile) ist:

$ file=/blaabla/bla/blah/foo.txt
echo $(basename ${file%.*}) # foo
Ron
quelle
Das ist eine nutzlose Verwendung vonecho . Im Allgemeinen echo $(command)ist es besser, einfach zu schreiben, es commandsei denn, Sie fordern die Shell ausdrücklich auf, commandvor der Anzeige des Ergebnisses eine Whitespace-Tokenisierung und eine Platzhaltererweiterung für die Ausgabe durchzuführen . Quiz: Was ist die Ausgabe von echo $(echo '*')(und wenn es das ist, was Sie wirklich wollen, wollen Sie wirklich wirklich nur echo *).
Tripleee
@triplee Ich habe überhaupt keinen echoBefehl verwendet. Ich habe es nur verwendet, um das Ergebnis zu demonstrieren, das fooin der 3. Zeile als Ergebnis der 2. Zeile erscheint.
Ron
Aber basename "${file%.*}"würde einfach das gleiche tun; Sie verwenden eine Befehlsersetzung, um die Ausgabe zu erfassen, und zwar nur für echodieselbe Ausgabe. (Ohne Zitat ist das Ergebnis nominell anders; aber das ist hier kaum relevant,
geschweige denn
Auch basename "$file" .txtvermeidet die Komplexität des Parameters Substitution.
Tripleee
1
@ Ron Lesen Sie seinen ersten Kommentar, bevor Sie ihn beschuldigen, unsere Zeit verschwendet zu haben.
frederick99
14

Ich denke, wenn Sie nur den Namen der Datei benötigen, können Sie dies versuchen:

FULLPATH=/usr/share/X11/xorg.conf.d/50-synaptics.conf

# Remove all the prefix until the "/" character
FILENAME=${FULLPATH##*/}

# Remove all the prefix until the "." character
FILEEXTENSION=${FILENAME##*.}

# Remove a suffix, in our case, the filename. This will return the name of the directory that contains this file.
BASEDIRECTORY=${FULLPATH%$FILENAME}

echo "path = $FULLPATH"
echo "file name = $FILENAME"
echo "file extension = $FILEEXTENSION"
echo "base directory = $BASEDIRECTORY"

Und das ist alles = D.

Andrew Woolfgang
quelle
Ich wollte nur BASEDIRECTORY :) Danke!
Carlos Ricardo
12

Sie können das Ausschneiden erzwingen, um alle Felder und nachfolgende Felder anzuzeigen, die -zur Feldnummer hinzugefügt werden .

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

Wenn also DATEI ist eth0.pcap.gz, wird die ERWEITERUNG seinpcap.gz

Mit der gleichen Logik können Sie den Dateinamen auch mit '-' mit dem folgenden Schnitt abrufen:

NAME=`basename "$FILE" | cut -d'.' -f-1`

Dies funktioniert auch für Dateinamen, die keine Erweiterung haben.

Maciek Gajewski
quelle
8

Magische Dateierkennung

Zusätzlich zu den vielen guten Antworten auf diese Frage zum Stapelüberlauf möchte ich hinzufügen:

Unter Linux und anderen Unixen gibt es einen magischen Befehl namens file, der die Dateityperkennung durch Analyse einiger erster Dateibytes durchführt. Dies ist ein sehr altes Tool, das ursprünglich für Druckserver verwendet wurde (wenn es nicht für ... erstellt wurde, da bin ich mir nicht sicher).

file myfile.txt
myfile.txt: UTF-8 Unicode text

file -b --mime-type myfile.txt
text/plain

Standarderweiterungen finden Sie in /etc/mime.types(auf meinem Debian GNU / Linux-Desktop. Siehe man fileund man mime.types. Möglicherweise müssen Sie das fileDienstprogramm und die mime-supportPakete installieren ):

grep $( file -b --mime-type myfile.txt ) </etc/mime.types
text/plain      asc txt text pot brf srt

Sie könnten eine erstellen Funktion zur Bestimmung der rechten Verlängerung. Es gibt eine kleine (nicht perfekte) Probe:

file2ext() {
    local _mimetype=$(file -Lb --mime-type "$1") _line _basemimetype
    case ${_mimetype##*[/.-]} in
        gzip | bzip2 | xz | z )
            _mimetype=${_mimetype##*[/.-]}
            _mimetype=${_mimetype//ip}
            _basemimetype=$(file -zLb --mime-type "$1")
            ;;
        stream )
            _mimetype=($(file -Lb "$1"))
            [ "${_mimetype[1]}" = "compressed" ] &&
                _basemimetype=$(file -b --mime-type - < <(
                        ${_mimetype,,} -d <"$1")) ||
                _basemimetype=${_mimetype,,}
            _mimetype=${_mimetype,,}
            ;;
        executable )  _mimetype='' _basemimetype='' ;;
        dosexec )     _mimetype='' _basemimetype='exe' ;;
        shellscript ) _mimetype='' _basemimetype='sh' ;;
        * )
            _basemimetype=$_mimetype
            _mimetype=''
            ;;
    esac
    while read -a _line ;do
        if [ "$_line" == "$_basemimetype" ] ;then
            [ "$_line[1]" ] &&
                _basemimetype=${_line[1]} ||
                _basemimetype=${_basemimetype##*[/.-]}
            break
        fi
        done </etc/mime.types
    case ${_basemimetype##*[/.-]} in
        executable ) _basemimetype='' ;;
        shellscript ) _basemimetype='sh' ;;
        dosexec ) _basemimetype='exe' ;;
        * ) ;;
    esac
    [ "$_mimetype" ] && [ "$_basemimetype" != "$_mimetype" ] &&
      printf ${2+-v} $2 "%s.%s" ${_basemimetype##*[/.-]} ${_mimetype##*[/.-]} ||
      printf ${2+-v} $2 "%s" ${_basemimetype##*[/.-]}
}

Diese Funktion kann eine Bash-Variable festlegen, die später verwendet werden kann:

(Dies ist inspiriert von der richtigen Antwort von @Petesh):

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

echo "$fullfile -> $filename . $extension"
F. Hauri
quelle
8

Ok, wenn ich das richtig verstehe, besteht das Problem hier darin, wie man den Namen und die vollständige Erweiterung einer Datei erhält, die mehrere Erweiterungen hat, z stuff.tar.gz.

Das funktioniert bei mir:

fullfile="stuff.tar.gz"
fileExt=${fullfile#*.}
fileName=${fullfile%*.$fileExt}

Dies gibt Ihnen stuffals Dateinamen und .tar.gzals Erweiterung. Es funktioniert für eine beliebige Anzahl von Erweiterungen, einschließlich 0. Ich hoffe, dies hilft allen, die das gleiche Problem haben =)

Al3xXx
quelle
Das richtige Ergebnis (je nachdem os.path.splitext, was das OP will) ist ('stuff.tar', '.gz').
Cyker
6

Ich benutze das folgende Skript

$ echo "foo.tar.gz"|rev|cut -d"." -f3-|rev
foo
Joydip Datta
quelle
Dies ist überhaupt nicht effizient. Zu oft zu gabeln, was ziemlich unnötig ist, da dieser Vorgang in reinem Bash ausgeführt werden kann, ohne dass externe Befehle und Gabeln erforderlich sind.
Codeforester
5
$ F = "text file.test.txt"  
$ echo ${F/*./}  
txt  

Dies ermöglicht mehrere Punkte und Leerzeichen in einem Dateinamen. Wenn jedoch keine Erweiterung vorhanden ist, wird der Dateiname selbst zurückgegeben. Leicht zu überprüfen; Testen Sie einfach, ob Dateiname und Erweiterung identisch sind.

Natürlich funktioniert diese Methode nicht für .tar.gz-Dateien. Dies könnte jedoch in einem zweistufigen Prozess erledigt werden. Wenn die Erweiterung gz ist, überprüfen Sie erneut, ob es auch eine Teererweiterung gibt.

Miriam Englisch
quelle
5

So extrahieren Sie den Dateinamen und die Erweiterung in Fischen :

function split-filename-extension --description "Prints the filename and extension"
  for file in $argv
    if test -f $file
      set --local extension (echo $file | awk -F. '{print $NF}')
      set --local filename (basename $file .$extension)
      echo "$filename $extension"
    else
      echo "$file is not a valid file"
    end
  end
end

Vorsichtsmaßnahmen: Teilung des letzten Punkts, was für Dateinamen mit Punkten gut geeignet ist, für Erweiterungen mit Punkten jedoch nicht. Siehe Beispiel unten.

Verwendungszweck:

$ split-filename-extension foo-0.4.2.zip bar.tar.gz
foo-0.4.2 zip  # Looks good!
bar.tar gz  # Careful, you probably want .tar.gz as the extension.

Es gibt wahrscheinlich bessere Möglichkeiten, dies zu tun. Fühlen Sie sich frei, meine Antwort zu bearbeiten, um sie zu verbessern.


Wenn es eine begrenzte Anzahl von Erweiterungen gibt, mit denen Sie sich befassen, und Sie alle kennen, versuchen Sie Folgendes:

switch $file
  case *.tar
    echo (basename $file .tar) tar
  case *.tar.bz2
    echo (basename $file .tar.bz2) tar.bz2
  case *.tar.gz
    echo (basename $file .tar.gz) tar.gz
  # and so on
end

Dies hat nicht die Einschränkung als erstes Beispiel, aber Sie müssen jeden Fall behandeln, damit es je nach der Anzahl der zu erwartenden Erweiterungen langwieriger werden kann.

Dennis
quelle
4

Hier ist Code mit AWK . Es kann einfacher gemacht werden. Aber ich bin nicht gut in AWK.

filename$ ls
abc.a.txt  a.b.c.txt  pp-kk.txt
filename$ find . -type f | awk -F/ '{print $2}' | rev | awk -F"." '{$1="";print}' | rev | awk 'gsub(" ",".") ,sub(".$", "")'
abc.a
a.b.c
pp-kk
filename$ find . -type f | awk -F/ '{print $2}' | awk -F"." '{print $NF}'
txt
txt
txt
Smilyface
quelle
Sie sollten die erste awk-Anweisung im letzten Beispiel nicht benötigen, oder?
BHSPitMonkey
Sie können vermeiden, Awk an Awk weiterzuleiten, indem Sie einen anderen Vorgang ausführen split(). awk -F / '{ n=split($2, a, "."); print a[n] }' uses / `als Trennzeichen der obersten Ebene, teilt dann aber die zweiten Felder auf .und druckt das letzte Element aus dem neuen Array.
Tripleee
4

Einfach benutzen ${parameter%word}

In Ihrem Fall:

${FILE%.*}

Wenn Sie es testen möchten, funktionieren alle folgenden Schritte und entfernen Sie einfach die Erweiterung:

FILE=abc.xyz; echo ${FILE%.*};
FILE=123.abc.xyz; echo ${FILE%.*};
FILE=abc; echo ${FILE%.*};
Enyo
quelle
2
Warum das Downvote? Es ist immer noch nützlich, obwohl es keine Leerzeichen um die =Schilder geben sollte.
SilverWolf - Stellen Sie Monica
1
Das funktioniert gut. Vielen Dank! (Jetzt hat es keine Leerzeichen um die Gleichheitszeichen, wenn es deshalb herabgestimmt wurde)
Alex. S.
3

Aufbauend auf der Antwort von Petesh können , wenn nur der Dateiname benötigt wird, sowohl Pfad als auch Erweiterung in einer einzigen Zeile entfernt werden.

filename=$(basename ${fullname%.*})
cvr
quelle
Hat bei mir nicht funktioniert: "basename: fehlender Operand Versuchen Sie 'basename --help' für weitere Informationen."
Helmy
Seltsam, sind Sie sicher, dass Sie Bash verwenden? In meinem Fall funktioniert es mit beiden Versionen 3.2.25 (altes CentOS) und 4.3.30 (Debian Jessie) einwandfrei.
CVR
Vielleicht ist im Dateinamen ein Leerzeichen? Versuchen Sie es mitfilename="$(basename "${fullname%.*}")"
Adrian
Das zweite Argument für basenameist optional, gibt jedoch die zu entfernende Erweiterung an. Die Substitution ist möglicherweise immer noch nützlich, ist es aber möglicherweise basenamenicht, da Sie alle diese Substitutionen mit Shell-Builtins durchführen können.
Tripleee
3

Basierend auf @ mklement0s exzellenten und voller zufälliger, nützlicher Bashismen - sowie anderen Antworten auf diese / andere Fragen / "das verdammte Internet" ... habe ich alles in ein wenig, etwas verständlicherem, zusammengefasst. wiederverwendbare Funktion für meine (oder Ihre) .bash_profile, die sich darum kümmert, was (ich denke) eine robustere Version von dirname/ basename/ was Sie haben sollte ..

function path { SAVEIFS=$IFS; IFS=""   # stash IFS for safe-keeping, etc.
    [[ $# != 2 ]] && echo "usage: path <path> <dir|name|fullname|ext>" && return    # demand 2 arguments
    [[ $1 =~ ^(.*/)?(.+)?$ ]] && {     # regex parse the path
        dir=${BASH_REMATCH[1]}
        file=${BASH_REMATCH[2]}
        ext=$([[ $file = *.* ]] && printf %s ${file##*.} || printf '')
        # edge cases for extensionless files and files like ".nesh_profile.coffee"
        [[ $file == $ext ]] && fnr=$file && ext='' || fnr=${file:0:$((${#file}-${#ext}))}
        case "$2" in
             dir) echo      "${dir%/*}"; ;;
            name) echo      "${fnr%.*}"; ;;
        fullname) echo "${fnr%.*}.$ext"; ;;
             ext) echo           "$ext"; ;;
        esac
    }
    IFS=$SAVEIFS
}     

Anwendungsbeispiele ...

SOMEPATH=/path/to.some/.random\ file.gzip
path $SOMEPATH dir        # /path/to.some
path $SOMEPATH name       # .random file
path $SOMEPATH ext        # gzip
path $SOMEPATH fullname   # .random file.gzip                     
path gobbledygook         # usage: -bash <path> <dir|name|fullname|ext>
Alex Gray
quelle
1
Schön gemacht; Einige Vorschläge: - Sie scheinen sich überhaupt nicht darauf zu verlassen $IFS(und wenn ja, könnten Sie localden Effekt der Einstellung lokalisieren). - Besser localVariablen verwenden. - Ihre Fehlermeldung sollte stderrnicht an stdout(use 1>&2) ausgegeben werden , und Sie sollten einen Exit-Code ungleich Null zurückgeben. - Besser umbenennen fullnamein basename(ersteres schlägt einen Pfad mit dir-Komponenten vor). - namefügt bedingungslos einen .(Punkt) hinzu, auch wenn das Original keinen hatte. Sie können das basenameDienstprogramm einfach verwenden , beachten Sie jedoch, dass eine Beendigung ignoriert wird /.
mklement0
2

Eine einfache Antwort:

Beachten Sie, dass Sie interessantere Muster erstellen können, um die Antwort auf die POSIX-Variablen zu erweitern . Für den hier beschriebenen Fall können Sie also einfach Folgendes tun:

tar -zxvf $1
cd ${1%.tar.*}

Dadurch wird das letzte Auftreten von .tar abgeschnitten. <etwas> .

Allgemeiner, wenn Sie das letzte Vorkommen von entfernen möchten. <etwas> . <something-else> dann

${1.*.*}

sollte gut funktionieren.

Der Link der obigen Antwort scheint tot zu sein. Hier finden Sie eine gute Erklärung für eine Reihe von Zeichenfolgenmanipulationen, die Sie direkt in Bash über TLDP ausführen können .

RandyP
quelle
Gibt es eine Möglichkeit, das Match unabhängig von Groß- und Kleinschreibung zu machen?
Tonix
2

Wenn Sie auch leere Erweiterungen zulassen möchten , ist dies die kürzeste, die ich mir vorstellen kann:

echo 'hello.txt' | sed -r 's/.+\.(.+)|.*/\1/' # EXTENSION
echo 'hello.txt' | sed -r 's/(.+)\..+|(.*)/\1\2/' # FILENAME

1. Zeile erklärt: Es stimmt mit PATH.EXT oder ANYTHING überein und ersetzt es durch EXT. Wenn ALLES übereinstimmt, wird die ext-Gruppe nicht erfasst.

phil294
quelle
2

Dies ist der einzige, der für mich funktioniert hat:

path='folder/other_folder/file.js'

base=${path##*/}
echo ${base%.*}

>> file

Dies kann auch bei der String-Interpolation verwendet werden, muss aber leider basevorher eingestellt werden.

Ken Mueller
quelle
1

Hier ist der Algorithmus, mit dem ich den Namen und die Erweiterung einer Datei gefunden habe, als ich ein Bash-Skript geschrieben habe, um Namen eindeutig zu machen, wenn Namen in Bezug auf das Gehäuse in Konflikt stehen.

#! /bin/bash 

#
# Finds 
# -- name and extension pairs
# -- null extension when there isn't an extension.
# -- Finds name of a hidden file without an extension
# 

declare -a fileNames=(
  '.Montreal' 
  '.Rome.txt' 
  'Loundon.txt' 
  'Paris' 
  'San Diego.txt'
  'San Francisco' 
  )

echo "Script ${0} finding name and extension pairs."
echo 

for theFileName in "${fileNames[@]}"
do
     echo "theFileName=${theFileName}"  

     # Get the proposed name by chopping off the extension
     name="${theFileName%.*}"

     # get extension.  Set to null when there isn't an extension
     # Thanks to mklement0 in a comment above.
     extension=$([[ "$theFileName" == *.* ]] && echo ".${theFileName##*.}" || echo '')

     # a hidden file without extenson?
     if [ "${theFileName}" = "${extension}" ] ; then
         # hidden file without extension.  Fixup.
         name=${theFileName}
         extension=""
     fi

     echo "  name=${name}"
     echo "  extension=${extension}"
done 

Der Testlauf.

$ config/Name\&Extension.bash 
Script config/Name&Extension.bash finding name and extension pairs.

theFileName=.Montreal
  name=.Montreal
  extension=
theFileName=.Rome.txt
  name=.Rome
  extension=.txt
theFileName=Loundon.txt
  name=Loundon
  extension=.txt
theFileName=Paris
  name=Paris
  extension=
theFileName=San Diego.txt
  name=San Diego
  extension=.txt
theFileName=San Francisco
  name=San Francisco
  extension=
$ 

Zu Ihrer Information: Das vollständige Transliterationsprogramm und weitere Testfälle finden Sie hier: https://www.dropbox.com/s/4c6m0f2e28a1vxf/avoid-clashes-code.zip?dl=0

historystamp
quelle
Von allen Lösungen ist dies die einzige, die eine leere Zeichenfolge zurückgibt, wenn die Datei keine Erweiterung hat mit:extension=$([[ "$theFileName" == *.* ]] && echo ".${theFileName##*.}" || echo '')
f0nzie
1

Verwenden Sie die Beispieldatei /Users/Jonathan/Scripts/bash/MyScript.sh, diesen Code:

MY_EXT=".${0##*.}"
ME=$(/usr/bin/basename "${0}" "${MY_EXT}")

wird zu ${ME}Sein MyScriptund ${MY_EXT}Sein führen .sh:


Skript:

#!/bin/bash
set -e

MY_EXT=".${0##*.}"
ME=$(/usr/bin/basename "${0}" "${MY_EXT}")

echo "${ME} - ${MY_EXT}"

Einige Tests:

$ ./MyScript.sh 
MyScript - .sh

$ bash MyScript.sh
MyScript - .sh

$ /Users/Jonathan/Scripts/bash/MyScript.sh
MyScript - .sh

$ bash /Users/Jonathan/Scripts/bash/MyScript.sh
MyScript - .sh
chown
quelle
2
Ich bin mir nicht sicher, warum dies so viele Abstimmungen hat - es ist tatsächlich effizienter als die akzeptierte Antwort. (Wie letzteres bricht es auch mit Eingabedateinamen ohne Erweiterung). Die Verwendung eines expliziten Pfades zu basenameist möglicherweise übertrieben.
mklement0
1

Aus den obigen Antworten ergibt sich der kürzeste Oneliner, der Pythons nachahmt

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

Angenommen, Ihre Datei hat wirklich eine Erweiterung

EXT="${PATH##*.}"; FILE=$(basename "$PATH" .$EXT)
Commonpike
quelle
Ich habe Downvotes dazu. Ich denke darüber nach, die Antwort zu entfernen, die Leute mögen sie irgendwie nicht.
Commonpike
Der Basisname entfernt nicht die Erweiterung, sondern nur den Pfad.
David Cullen
Es ist so lange her, dass ich mir die Manpage angesehen habe, dass ich die Option SUFFIX vergessen habe.
David Cullen
Sie müssen wissen, welche Erweiterung Sie entfernen möchten, bevor Sie wissen, was Sie einfügen müssen, EXTdamit es sich um Schildkröten handelt. (Außerdem sollten Sie alle Großbuchstaben für Ihre privaten Variablennamen vermeiden; sie sind für Systemvariablen reserviert.)
Tripleee