it-swarm.com.de

Extrahieren Sie den Dateinamen und die Erweiterung in Bash

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 dies nicht funktioniert, wenn der Dateiname mehrere . Zeichen enthält. Wenn ich beispielsweise a.b.js habe, werden a und b.js anstelle von a.b und js berücksichtigt.

Es kann einfach in Python mit gemacht werden

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

aber ich würde es vorziehen, einen Python -Interpreter nicht nur dafür zu starten, wenn möglich.

Irgendwelche besseren Ideen?

1905
ibz

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 Pfades konzentrieren, anstatt auf das '.' Das sollte funktionieren, auch wenn Sie unvorhersehbare Dateierweiterungen haben:

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

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

3223
Petesh
~% FILE="example.tar.gz"
~% echo "${FILE%%.*}"
example
~% echo "${FILE%.*}"
example.tar
~% echo "${FILE#*.}"
tar.gz
~% echo "${FILE##*.}"
gz

Weitere Informationen finden Sie unter Shell-Parametererweiterung im Bash-Handbuch.

589
Juliano

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

basename filename .extension

zum Beispiel:

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

und wir bekommen

filename
355
Tomi Po

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

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

Wenn Ihr Dateiname die Form ./somefile.tar.gz hätte, dann würde echo ${FILENAME%%.*} die längste Übereinstimmung mit . gierig entfernen und Sie hätten die leere Zeichenfolge.

(Sie können das 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
135
sotapme

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

#!/bin/bash
for fullpath in "[email protected]"
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 = "" 
68
Doctor J

Sie können basename verwenden.

Beispiel:

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

Sie müssen basename mit der Erweiterung angeben, die entfernt werden soll. Wenn Sie jedoch immer tar mit -z ausführen, wissen Sie, dass die Erweiterung .tar.gz lautet.

Dies sollte tun, was Sie wollen:

tar -zxvf $1
cd $(basename $1 .tar.gz)
43
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 für NAME ersetzt ein "." -Zeichen gefolgt von einer beliebigen Anzahl von Nicht-"." -Zeichen bis zum Ende der Zeile mit nichts (dh er entfernt alles aus dem endgültigen "." bis zum Ende der Zeile (einschließlich). Dies ist im Grunde eine nicht gierige Substitution mit Regex-Trick.

Der Befehl für EXTENSION ersetzt eine beliebige Anzahl von Zeichen, gefolgt von einem "." -Zeichen am Zeilenanfang, mit nichts (dh er entfernt alles vom Zeilenanfang bis einschließlich zum letzten Punkt) ). Dies ist eine gierige Substitution, die die Standardaktion ist.

31
paxdiablo

Mellen schreibt in einem Kommentar zu einem Blogbeitrag:

Unter Verwendung von Bash gibt es auch ${file%.*}, um den Dateinamen ohne die Erweiterung abzurufen, und ${file##*.}, um die Erweiterung alleine abzurufen. Das ist,

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

Ausgänge:

filename: thisfile
extension: txt
27
Kebabbert

Sie können den Befehl cut verwenden, um die letzten beiden Erweiterungen (den Teil ".tar.gz") zu entfernen:

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

Wie Clayton Hughes in einem Kommentar feststellte, funktioniert dies nicht für das tatsächliche Beispiel in der Frage. Als Alternative schlage ich vor, sed mit 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]

25

Keine Notwendigkeit, sich mit awk oder sed oder sogar Perl für diese einfache Aufgabe zu beschäftigen. Es gibt eine pure-Bash, os.path.splitext()-kompatible Lösung, die nur Parametererweiterungen verwendet.

Referenzimplementierung

Dokumentation von os.path.splitext(path) :

Teilen Sie den Pfadnamen path in ein Paar (root, ext) auf, sodass root + ext == path und ext leer sind oder mit einem Punkt beginnen und at enthalten die meisten eine Periode. Anführungszeichen auf dem Basisnamen werden ignoriert. splitext('.cshrc') gibt ('.cshrc', '') zurück.

Python-Code:

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

Bash-Implementierung

Führungsperioden einhalten

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

Führungsperioden ignorieren

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

Tests

Hier sind Testfälle für die Implementierung Ignorieren der führenden Perioden , die bei jeder Eingabe mit der Referenzimplementierung Python ü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.

25
Cyker

Hier finden Sie einige alternative Vorschläge (hauptsächlich in awk), einschließlich einiger erweiterter Anwendungsfälle, z. B. 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 '^[/]\|^~/'

In allen Anwendungsfällen wird der ursprüngliche vollständige Pfad als Eingabe verwendet, ohne von Zwischenergebnissen abzuhängen.

21
henfiber

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

$ file=/blaabla/bla/blah/foo.txt
echo $(basename ${file%.*}) # foo
16
Ron

Die akzeptierte Antwort funktioniert gut in typischen Fällen , aber schlägt fehl in Edge Fällen , nämlich:

  • Bei Dateinamen ohne Erweiterung (im Rest dieser Antwort Suffix genannt) gibt extension=${filename##*.} den eingegebenen Dateinamen und nicht eine leere Zeichenfolge zurück.
  • extension=${filename##*.} enthält entgegen der Konvention nicht den anfänglichen ..
    • Das blinde Voranstellen von . würde für Dateinamen ohne Suffix nicht funktionieren.
  • filename="${filename%.*}" ist die leere Zeichenfolge, wenn der Name der Eingabedatei entgegen der Konvention mit . beginnt und keine weiteren . Zeichen enthält (z. B. .bash_profile).

---------

Die Komplexität einer robusten Lösung, die alle Edge-Fä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'

Beachten Sie, dass die Argumente nach dem Eingabepfad frei gewählt werden, Positionsvariable Namen.
Geben Sie _ (zur Verwendung der Wegwerfvariablen $_) oder '' an, um nicht interessierende Variablen zu überspringen. Verwenden Sie beispielsweise splitPath '/etc/bash.bashrc' _ _ fnameroot extension, um nur das Stammverzeichnis und die Erweiterung zu extrahieren.


# 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 Edge-Fälle:

  • ein Dateiname ohne Suffix
  • ein Dateiname, der mit . beginnt (nicht ​​gilt als Beginn des Suffix)
  • ein Eingabepfad, der mit / endet (abschließender / wird ignoriert)
  • ein Eingabepfad, der nur ein Dateiname ist (. wird als übergeordneter Pfad zurückgegeben)
  • ein Dateiname mit mehr als .- vorangestelltem 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
16
mklement0

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.

12

Sie können cut zwingen, alle und nachfolgende Felder anzuzeigen, indem Sie der Feldnummer - hinzufügen.

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

Wenn FILE also eth0.pcap.gz ist, lautet die ERWEITERUNG pcap.gz

Mit der gleichen Logik können Sie den Dateinamen auch mit '-' mit cut wie folgt abrufen:

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

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

10
maciek gajewski

Wenn ich das richtig verstehe, besteht das Problem darin, wie der Name und die vollständige Erweiterung einer Datei mit mehreren Erweiterungen abgerufen werden können, z. B. stuff.tar.gz.

Das funktioniert bei mir:

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

Dadurch erhalten Sie stuff als Dateinamen und .tar.gz als Erweiterung. Es funktioniert für eine beliebige Anzahl von Erweiterungen, einschließlich 0. Ich hoffe, dies hilft jedem, der das gleiche Problem hat =)

7
Al3xXx

Magic File Erkennung

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

Unter Linux und anderen Unixen gibt es einen magischen Befehl mit dem Namen file, der die Dateityperkennung durch Analysieren einiger erster Bytes der Datei durchführt. Dies ist ein sehr altes Tool, das ursprünglich für Druckserver verwendet wurde (wenn es nicht für ... Ich bin mir nicht sicher).

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

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

Standards-Erweiterungen finden Sie in _/etc/mime.types_ (auf meinem Debian GNU/Linux-Desktop. Siehe _man file_ und _man mime.types_. Möglicherweise müssen Sie das Dienstprogramm file installieren und _mime-support_ Pakete):

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

Sie könnten eine bash - Funktion erstellen, um die richtige Erweiterung zu bestimmen. Es gibt ein kleines (nicht perfektes) Beispiel:

_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 @Petesh richtige Antwort):

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

echo "$fullfile -> $filename . $extension"
_
7
F. Hauri

Ich benutze das folgende Skript

$ echo "foo.tar.gz"|rev|cut -d"." -f3-|rev
foo
6
Joydip Datta
$ F = "text file.test.txt"  
$ echo ${F/*./}  
txt  

Dies berücksichtigt 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 der Dateiname und die Erweiterung identisch sind.

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

5
Miriam English

So extrahieren Sie den Dateinamen und die Erweiterung in fish :

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: Teilt den letzten Punkt auf, was gut für Dateinamen mit Punkten funktioniert, aber nicht gut für Erweiterungen mit Punkten. Siehe folgendes Beispiel.

Verwendung:

$ 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 zu tun haben, 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 den Vorbehalt als erstes Beispiel , aber Sie müssen jeden Fall behandeln, so dass es je nach Anzahl der möglichen Erweiterungen langwieriger sein kann erwarten von.

4
Dennis

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
4
smilyface

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

filename=$(basename ${fullname%.*})
3
cvr

Benutze einfach ${parameter%Word}

In deinem Fall:

${FILE%.*}

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

FILE=abc.xyz; echo ${FILE%.*};
FILE=123.abc.xyz; echo ${FILE%.*};
FILE=abc; echo ${FILE%.*};
3
enyo

Basiert größtenteils auf @ mklement0s exzellenten und vollgepackten zufälligen, nützlichen Bashismen - sowie anderen Antworten auf diese/andere Fragen/", die verflixt sind internet "... Ich habe alles in eine kleine, etwas verständlichere, wiederverwendbare Funktion für meinen (oder Ihren) .bash_profile verpackt kümmert sich darum, was (meiner Meinung nach) eine robustere Version von dirname/basename/ sein soll, was Sie haben.

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>
2
Alex Gray

Wenn Sie auch leer Erweiterungen zulassen möchten, ist dies die kürzeste, die ich mir einfallen lassen könnte:

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 ANYTHING übereinstimmt, wird die ext-Gruppe nicht erfasst.

2
Blauhirn

Eine einfache Antwort:

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

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

Damit wird das letzte Vorkommen von .tar. <something> abgeschnitten.

Im Allgemeinen, wenn Sie das letzte Vorkommen von. <something> . <something-else> entfernen möchten

${1.*.*}

sollte gut funktionieren.

Der Link der obigen Antwort scheint tot zu sein. Hier ist eine großartige Erklärung für einige der Zeichenfolgenmanipulationen, die Sie direkt in Bash über TLDP ausführen können. .

2
RandyP

Dies ist die einzige, die 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, aber leider müssen Sie base vorher einstellen.

1
Ken Mueller

Aus den obigen Antworten geht hervor, dass es sich um den kürzesten Oneliner handelt, der Pythons nachahmt

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

angenommen, Ihre Datei hat wirklich eine Erweiterung, ist

EXT="${PATH##*.}"; FILE=$(basename "$PATH" .$EXT)
1
commonpike

IMHO wurde bereits die beste Lösung angegeben (unter Verwendung der Shell-Parametererweiterung) und sie ist zu diesem Zeitpunkt die bestbewertete.

Ich füge jedoch dieses hinzu, das nur dumme Befehle verwendet, das nicht effizient ist und das niemand ernsthaft verwenden sollte:

FILENAME=$(echo $FILE | cut -d . -f 1-$(printf $FILE | tr . '\n' | wc -l))
EXTENSION=$(echo $FILE | tr . '\n' | tail -1)

Hinzugefügt nur zum Spaß :-)

1
Bruno BEAUFILS

Unter Verwendung der Beispieldatei /Users/Jonathan/Scripts/bash/MyScript.sh lautet dieser Code:

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

führt dazu, dass ${ME}MyScript und ${MY_EXT}.sh ist:


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
1
chown

Vielleicht gibt es eine Option in tar, um dies zu tun; Hast du den Mann überprüft? Andernfalls können Sie Bash-String-Erweiterung verwenden:

test="mpc-1.0.1.tar.gz"
noExt="${test/.tar.gz/}" # Remove the string '.tar.gz'
echo $noExt
0
Édouard Lopez

Um dir nützlicher zu machen (für den Fall, dass eine lokale Datei ohne Pfad als Eingabe angegeben wird), habe ich Folgendes getan:

# Substring from 0 thru pos of filename
dir="${fullpath:0:${#fullpath} - ${#filename}}"
if [[ -z "$dir" ]]; then
    dir="./"
fi

Auf diese Weise können Sie beispielsweise ein Suffix zum Basisnamen der Eingabedatei hinzufügen:

outfile=${dir}${base}_suffix.${ext}

testcase: foo.bar
dir: "./"
base: "foo"
ext: "bar"
outfile: "./foo_suffix.bar"

testcase: /home/me/foo.bar
dir: "/home/me/"
base: "foo"
ext: "bar"
outfile: "/home/me/foo_suffix.bar"
0
Bill Gale

Hier ist der Algorithmus, den ich verwendet habe, um den Namen und die Erweiterung einer Datei zu finden, als ich ein Bash-Skript geschrieben habe, um Namen eindeutig zu machen, wenn Konflikte mit der Schreibweise auftreten.

#! /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

Sie können verwenden

sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-

dateiname und

sed 's/^/./' | rev | cut -d. -f1  | rev

verlängerung bekommen.

Testfall:

echo "filename.gz"     | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename.gz"     | sed 's/^/./' | rev | cut -d. -f1  | rev
echo "filename"        | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename"        | sed 's/^/./' | rev | cut -d. -f1  | rev
echo "filename.tar.gz" | sed 's/^/./' | rev | cut -d. -f2- | rev | cut -c2-
echo "filename.tar.gz" | sed 's/^/./' | rev | cut -d. -f1  | rev
0

Hier ist eine sed-Lösung, die Pfadkomponenten in verschiedenen Formen extrahiert und die meisten Edge-Fälle verarbeitet:

_## Enter the input path and field separator character, for example:
## (separatorChar must not be present in inputPath)

inputPath="/path/to/Foo.bar"
separatorChar=":"

## sed extracts the path components and assigns them to output variables

oldIFS="$IFS"
IFS="$separatorChar"
read dirPathWithSlash dirPath fileNameWithExt fileName fileExtWithDot fileExt <<<"$(sed -En '
s/^[[:space:]]+//
s/[[:space:]]+$//
t l1
:l1
s/^([^/]|$)//
t
s/[/]+$//
t l2
:l2
s/^$/filesystem\/\
filesystem/p
t
h
s/^(.*)([/])([^/]+)$/\1\2\
\1\
\3/p
g
t l3
:l3
s/^.*[/]([^/]+)([.])([a-zA-Z0-9]+)$/\1\
\2\3\
\3/p
t
s/^.*[/](.+)$/\1/p
' <<<"$inputPath" | tr "\n" "$separatorChar")"
IFS="$oldIFS"

## Results (all use separatorChar=":")

## inputPath        = /path/to/Foo.bar
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = Foo.bar
## fileName         = Foo
## fileExtWithDot   = .bar
## fileExt          = bar

## inputPath        = /path/to/Foobar
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = Foobar
## fileName         = Foobar
## fileExtWithDot   =
## fileExt          =

## inputPath        = /path/to/...bar
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = ...bar
## fileName         = ..
## fileExtWithDot   = .bar
## fileExt          = bar

## inputPath        = /path/to/..bar
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = ..bar
## fileName         = .
## fileExtWithDot   = .bar
## fileExt          = bar

## inputPath        = /path/to/.bar
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = .bar
## fileName         = .bar
## fileExtWithDot   = 
## fileExt          = 

## inputPath        = /path/to/...
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = ...
## fileName         = ...
## fileExtWithDot   =
## fileExt          =

## inputPath        = /path/to/Foo.
## dirPathWithSlash = /path/to/
## dirPath          = /path/to 
## fileNameWithExt  = Foo.
## fileName         = Foo.
## fileExtWithDot   =
## fileExt          =

## inputPath        = / (the root directory)
## dirPathWithSlash = filesystem/
## dirPath          = filesystem
## fileNameWithExt  =
## fileName         =
## fileExtWithDot   =
## fileExt          =

## inputPath        =  (invalid because empty)
## dirPathWithSlash =
## dirPath          =
## fileNameWithExt  =
## fileName         =
## fileExtWithDot   =
## fileExt          =

## inputPath        = Foo/bar (invalid because doesn't start with a forward slash)
## dirPathWithSlash =
## dirPath          =
## fileNameWithExt  =
## fileName         =
## fileExtWithDot   =
## fileExt          =
_

So funktioniert das:

sed analysiert den Eingabepfad und druckt die folgenden Pfadkomponenten nacheinander in separaten Zeilen aus:

  • verzeichnispfad mit einem abschließenden Schrägstrich
  • verzeichnispfad ohne abschließenden Schrägstrich
  • dateiname mit Endung
  • dateiname ohne Endung
  • dateierweiterung mit einem führenden Punkt
  • dateierweiterung ohne führendes Punktzeichen

tr konvertiert die sed Ausgabe in eine durch Trennzeichen getrennte Zeichenfolge der obigen Pfadkomponenten.

read verwendet das Trennzeichen als Feldtrennzeichen (_IFS="$separatorChar"_) und weist jede der Pfadkomponenten ihrer jeweiligen Variablen zu.

So funktioniert das Konstrukt sed:

  • s/^[[:space:]]+// und s/[[:space:]]+$// entfernen führende und/oder nachfolgende Leerzeichen
  • t l1 und :l1 aktualisieren die Funktion t für die nächste Funktion s
  • s/^([^/]|$)// und t prüfen auf einen ungültigen Eingabepfad (der nicht mit einem Schrägstrich beginnt). In diesem Fall lassen sie alle Ausgabezeilen leer und beenden den Befehl sed
  • s/[/]+$// entfernt alle abschließenden Schrägstriche
  • t l2 und :l2 aktualisieren die Funktion t für die nächste Funktion s
  • s/^$/filesystem\/\\[newline]filesystem/p und t prüfen, ob der Eingabepfad aus dem Stammverzeichnis / besteht. In diesem Fall wird Folgendes ausgegeben: filesystem / und filesystem für die Ausgabezeilen dirPathWithSlash und dirPath, lässt alle anderen Ausgabezeilen leer und beendet das sed Befehl
  • h speichert den Eingabepfad im Hold-Bereich
  • s/^(.*)([/])([^/]+)$/\1\2\\[newline]\1\\[newline]\3/p gibt die Ausgabezeilen dirPathWithSlash, dirPath und fileNameWithExt aus
  • g ruft den Eingabepfad aus dem Haltebereich ab
  • t l3 und :l3 aktualisieren die Funktion t für die nächste Funktion s
  • s/^.*\[/]([^/]+)([.])([a-zA-Z0-9]+)$/\1\\[newline]\2\3\\[newline]\3/p und t geben die Ausgabezeilen fileName, fileExtWithDot und fileExt für das aus In einem Fall, in dem eine Dateierweiterung vorhanden ist (die nur aus alphanumerischen Zeichen besteht), wird der Befehl sed beendet
  • s/^.*\[/](.+)$/\1/p gibt die Ausgabezeilen fileName, aber nicht fileExtWithDot und fileExt für den Fall aus, dass eine Dateierweiterung nicht vorhanden ist Existiert und beendet dann den Befehl sed.
0
scolfax