Wyodrębnij nazwę pliku i rozszerzenie w Bash

Chcę pobrać nazwę pliku (bez rozszerzenia) i rozszerzenie osobno.

Najlepsze rozwiązanie jakie dotychczas znalazłem to:

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

Jest to złe, ponieważ nie działa, jeśli nazwa pliku zawiera wiele znaków {[2] }. Jeśli, powiedzmy, mam a.b.js, rozważy a i b.js, zamiast a.b i js.

Można to łatwo zrobić w Pythonie za pomocą

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

Ale wolałbym nie uruchamiać interpretera Pythona tylko w tym celu, jeśli to możliwe.

Any lepsze pomysły?

Author: Benjamin W., 2009-06-08

30 answers

Najpierw pobierz nazwę pliku bez ścieżki:

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

Alternatywnie, możesz skupić się na ostatnim '/' ścieżki zamiast na '.'które powinno działać nawet jeśli masz nieprzewidywalne rozszerzenia plików:

filename="${fullfile##*/}"
 2875
Author: Petesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-30 13:58:25
~% FILE="example.tar.gz"
~% echo "${FILE%%.*}"
example
~% echo "${FILE%.*}"
example.tar
~% echo "${FILE#*.}"
tar.gz
~% echo "${FILE##*.}"
gz

Aby uzyskać więcej szczegółów, zobacz rozszerzenie parametrów powłoki w podręczniku Bash.

 511
Author: Juliano,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-20 16:58:03

Zazwyczaj znasz już rozszerzenie, więc możesz chcieć użyć:

basename filename .extension

Na przykład:

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

I otrzymujemy

filename
 288
Author: Tomi Po,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-19 10:56:09

Możesz użyć magii zmiennych POSIX:

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

Jest zastrzeżenie, że jeśli Twoja nazwa pliku ma postać ./somefile.tar.gz, to echo ${FILENAME%%.*} chciwie usunie najdłuższy pasujący do . i będziesz miał pusty łańcuch.

(możesz obejść to za pomocą zmiennej tymczasowej:

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

)


Ta strona wyjaśnia więcej.

${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
 125
Author: sotapme,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-05 15:40:05

To nie działa, jeśli plik nie ma rozszerzenia lub nazwy pliku. Oto, czego używam; używa tylko wbudowanych i obsługuje więcej (ale nie wszystkie) patologicznych nazw plików.

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

A oto kilka testów:

$ 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  = ""
 65
Author: Doctor J,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-10 06:13:08

Możesz użyć basename.

Przykład:

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

Musisz podać basename z rozszerzeniem, które zostanie usunięte, jednak jeśli zawsze wykonujesz tar z -z, to wiesz, że rozszerzenie będzie .tar.gz.

To powinno robić to, co chcesz:

tar -zxvf $1
cd $(basename $1 .tar.gz)
 41
Author: Bjarke Freund-Hansen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-05 08:56:05
pax> echo a.b.js | sed 's/\.[^.]*$//'
a.b
pax> echo a.b.js | sed 's/^.*\.//'
js

Działa dobrze, więc możesz po prostu użyć:

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

Przy okazji komendy działają w następujący sposób.

Polecenie dla NAME zastępuje znak ".", po którym następuje dowolna liczba znaków nie-{[3] } aż do końca linii, z niczym(tzn. usuwa wszystko od ostatniego "." do końca linii, włącznie). Jest to w zasadzie nie chciwe podstawienie za pomocą sztuczek regex.

Polecenie dla EXTENSION zastępuje dowolną liczbę znaków, po której następuje "." znak na początku linii, z niczym (tzn. usuwa wszystko od początku linii do ostatniej kropki, włącznie). Jest to zachłanna substytucja, która jest domyślną akcją.

 26
Author: paxdiablo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-05 01:07:22

Mellen pisze w komentarzu do wpisu na blogu:

Używając Bash, istnieje również ${file%.*}, aby uzyskać nazwę pliku bez rozszerzenia i ${file##*.}, aby uzyskać samo rozszerzenie. Czyli

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

Wyjścia:

filename: thisfile
extension: txt
 24
Author: Kebabbert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-20 16:58:54

Możesz użyć cut polecenie usunięcia dwóch ostatnich rozszerzeń (część ".tar.gz"):

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

Jak zauważył Clayton Hughes w komentarzu, nie będzie to działać dla rzeczywistego przykładu w pytaniu. Więc jako alternatywę proponuję użycie sed z rozszerzonymi wyrażeniami regularnymi, takimi jak:

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

Działa poprzez bezwarunkowe usunięcie dwóch ostatnich (alfanumerycznych) rozszerzeń.

[Zaktualizowano ponownie po komentarzu od Andersa Lindahla]

 22
Author: Some programmer dude,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-04 08:08:34

Nie trzeba przejmować się awk lub sed lub nawet perl dla tego prostego zadania. Istnieje rozwiązanie pure-Bash, kompatybilne z os.path.splitext(), które używa tylko rozszerzeń parametrów.

Implementacja Odniesienia

Dokumentacja os.path.splitext(path):

Podziel ścieżkę na parę (root, ext) taką, że root + ext == path, a ext jest pusty lub zaczyna się kropką i zawiera co najwyżej jeden kropkę. Pierwsze okresy w nazwie bazowej są ignorowane; splitext('.cshrc') zwraca ('.cshrc', '').

Kod Pythona:

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

Implementacja Bash

Honorowanie okresów wiodących

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

Ignorowanie okresów czołowych

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

Testy

Oto przypadki testowe dla implementacji ignorującej okresy wiodące , która powinna pasować do implementacji referencyjnej Pythona na każdym wejściu.

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

Wyniki Badań

Wszystkie testy przeszły pomyślnie.

 22
Author: Cyker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-19 23:35:04

Oto kilka alternatywnych sugestii (głównie w awk), w tym kilka zaawansowanych przypadków użycia, takich jak wyodrębnianie numerów wersji pakietów oprogramowania.

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 '^[/]\|^~/'

Wszystkie przypadki użycia używają oryginalnej pełnej ścieżki jako danych wejściowych, bez zależności od wyników pośrednich.

 20
Author: henfiber,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-16 09:02:52

[zmieniono z jednowierszowej na ogólną funkcję bash , zachowanie zgodne z narzędziami dirname i basename; Dodano uzasadnienie.]

The akceptowana odpowiedź działa dobrze w typowych przypadkach , Ale zawodzi w edge } przypadkach , a mianowicie:

  • dla nazw plików bez rozszerzenia (wywołanych przyrostkiem w pozostałej części odpowiedzi), extension=${filename##*.} zwraca nazwę pliku wejściowego zamiast pustego łańcucha.
  • extension=${filename##*.} nie zawiera inicjału ., wbrew konwencji.
    • ślepe poprzedzanie . nie zadziała dla nazw plików bez sufiksu.
  • filename="${filename%.*}" będzie pustym łańcuchem znaków, jeśli nazwa pliku wejściowego zaczyna się od . i nie zawiera dalszych znaków . (np. .bash_profile) - wbrew konwencji.

---------

Tak więc złożoność rozwiązania obejmującego wszystkie przypadki brzegowe wymaga funkcji - zobacz jej definicja poniżej; to może zwrócić wszystkie Składniki ścieżki.

Przykładowe wywołanie:

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

Zauważ, że argumenty po ścieżce wejściowej są dowolnie wybierane, pozycyjne zmienne nazwy .
Aby pominąć zmienne, które nie są interesujące, przed tymi, które są, określ _ (aby użyć zmiennej throw-away $_) lub ''; np. aby wyodrębnić tylko root i rozszerzenie nazwy pliku, użyj splitPath '/etc/bash.bashrc' _ _ fnameroot extension.


# 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

Kod testowy, który ćwiczy funkcja:

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

Oczekiwane wyjście-zwróć uwagę na przypadki krawędzi:

  • nazwa pliku bez przyrostka
  • nazwa pliku zaczynająca się od . (Nie uważany za początek sufiksu)
  • Nie jest to jednak możliwe, ponieważ nie jest to możliwe.]}
  • ścieżka wejściowa, która jest tylko nazwą pliku (. jest zwracana jako ścieżka nadrzędna)
  • nazwa pliku, który ma więcej niż .-przedrostek tokena (tylko ostatni jest uważany za sufiks):
----- /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
 12
Author: mklement0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:43

Myślę, że jeśli potrzebujesz tylko nazwy pliku, możesz spróbować tego:

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"

I to wszystko =D.

 12
Author: Andrew Woolfgang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-20 17:00:14

Najmniejsze i najprostsze rozwiązanie (w jednej linii) to:

$ file=/blaabla/bla/blah/foo.txt

echo $(basename ${file%.*})

foo

 12
Author: Ron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-22 14:39:16

Możesz wymusić wycięcie, aby wyświetlić wszystkie pola i kolejne dodając - do numeru pola.

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

Więc jeśli plik jest eth0.pcap.gz, rozszerzenie będzie pcap.gz

Używając tej samej logiki, możesz również pobrać nazwę pliku używając ' - ' z wyciętym w następujący sposób:

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

Działa to nawet dla nazw plików, które nie mają rozszerzenia.

 9
Author: maciek gajewski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-30 16:23:11

Ok więc jeśli dobrze rozumiem, problem polega na tym, jak zdobyć nazwę i pełne rozszerzenie pliku, który ma wiele rozszerzeń, np. stuff.tar.gz.

To działa dla mnie:

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

To daje stuff jako nazwę pliku i .tar.gz jako rozszerzenie. Działa dla dowolnej liczby rozszerzeń, w tym 0. Mam nadzieję, że to pomoże każdemu, kto ma ten sam problem =)

 7
Author: Al3xXx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-02 10:26:40

Magiczne rozpoznawanie plików

Oprócz wielu dobrych odpowiedzi na to pytanie o przepełnienie stosu chciałbym dodać:

Pod Linuksem i innymi unixenami istnieje magiczne polecenie o nazwie file, które wykrywa typ pliku analizując niektóre pierwsze bajty pliku. Jest to bardzo stare narzędzie, początkowo używane dla serwerów wydruku (jeśli nie zostało stworzone dla... Nie jestem tego pewien).

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

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

Rozszerzenia standardów można znaleźć w /etc/mime.types (na moim Debianie GNU / Linux desktop. Zob. man file i man mime.types. Być może musisz zainstalować pakiety file utility i mime-support):

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

Możesz utworzyć funkcję bash do określania WŁAŚCIWEGO rozszerzenia. Jest mała (nie idealna) próbka:

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##*[/.-]}
}

Ta funkcja może ustawić zmienną Bash, która może być użyta później:

(to jest inspirowane @Petesh right answer):

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

echo "$fullfile -> $filename . $extension"
 6
Author: F. Hauri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-20 17:04:11
$ F = "text file.test.txt"  
$ echo ${F/*./}  
txt  

Ta funkcja obsługuje wiele kropek i spacji w nazwie pliku, Jednak jeśli nie ma rozszerzenia, zwraca samą nazwę pliku. Łatwo jednak sprawdzić, czy nazwa pliku i rozszerzenie są takie same.

Oczywiście ta metoda nie działa .smoła.pliki gz. Można to jednak przeprowadzić w dwuetapowym procesie. Jeśli rozszerzeniem jest gz, sprawdź ponownie, czy istnieje również rozszerzenie tar.

 4
Author: Miriam English,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-10 13:58:28

Używam następującego skryptu

$ echo "foo.tar.gz"|rev|cut -d"." -f3-|rev
foo
 4
Author: Joydip Datta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-22 12:56:10

Jak wyodrębnić nazwę pliku i rozszerzenie w ryby:

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

Caveats: dzieli się na ostatnią kropkę, co działa dobrze dla nazw plików z kropkami w nich, ale nie dobrze dla rozszerzeń z kropkami w nich. Zobacz przykład poniżej.

Użycie:

$ 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.
Są na to chyba lepsze sposoby. Zapraszam do edycji mojej odpowiedzi, aby ją poprawić.
Jeśli masz do czynienia z ograniczonym zestawem rozszerzeń i znasz je wszystkie, spróbuj to:
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

To nie ma zastrzeżenie jako pierwszy przykład, ale musisz zająć się każdą sprawą, więc może być bardziej żmudne w zależności od tego, ile rozszerzeń możesz się spodziewać.

 4
Author: Dennis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-30 21:41:03

Oto kod z AWK . Można to zrobić prościej. Ale nie jestem dobry w 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
 3
Author: smilyface,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-20 17:00:58

Prosta odpowiedź:

Aby rozszerzyć na zmienne POSIX odpowiadają , zauważ, że możesz zrobić więcej ciekawych wzorców. Tak więc w przypadku opisanym tutaj, możesz po prostu zrobić to:

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

To odetnie ostatnie wystąpienie .smoła..

Bardziej ogólnie, jeśli chcesz usunąć ostatnie wystąpienie .. then

${1.*.*}
Powinno działać dobrze. [[2]] Link powyższa odpowiedź wygląda na martwego. Oto świetne wyjaśnienie kilku manipulacji łańcuchami, które możesz zrobić bezpośrednio w Bash, z TLDP .
 2
Author: sqjoatmos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-20 17:07:49

Budowanie z Petesh odpowiedź, jeśli potrzebna jest tylko nazwa pliku, zarówno ścieżka, jak i rozszerzenie mogą być usunięte w jednej linii,

filename=$(basename ${fullname%.*})
 2
Author: cvr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:45

Po prostu użyj ${parameter%word}

W Twoim przypadku:

${FILE%.*}

Jeśli chcesz go przetestować, wykonaj następujące czynności i po prostu usuń rozszerzenie:

FILE=abc.xyz; echo ${FILE%.*};
FILE=123.abc.xyz; echo ${FILE%.*};
FILE=abc; echo ${FILE%.*};
 2
Author: enyo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-14 15:48:43

Oparty w dużej mierze na doskonałych, a zarazem pełnych losowych, użytecznych bashismach -a także innych odpowiedzi na to / inne pytania / "ten cholerny internet"... I owinął to wszystko w trochę, nieco bardziej zrozumiałe, wielokrotnego użytku Funkcja dla mojego (lub Twój) .bash_profile, która zajmuje się tym, co (uważam) powinna być bardziej solidna wersja dirname/basename / co masz ..

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 extesionless 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
}     

Przykłady użycia...

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>
 1
Author: Alex Gray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-27 02:18:45

Z powyższych odpowiedzi wynika, że najkrótszy z nich naśladuje Pythona

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

Zakładając, że Twój plik naprawdę ma rozszerzenie, to

EXT="${PATH##*.}"; FILE=$(basename "$PATH" .$EXT)
 1
Author: commonpike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-20 17:04:50

Jeśli chcesz również zezwolić na puste rozszerzenia, jest to najkrótsze, jakie mogłem wymyślić:

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

Pierwsza linia wyjaśniona: pasuje do ścieżki.EXT lub cokolwiek i zastępuje go EXT. Jeśli cokolwiek zostało dopasowane, Grupa ext nie jest przechwytywana.

 1
Author: Blauhirn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-17 16:47:03

W celu uĺźytecznoĺ "Ci katalogu (w przypadku gdy plik lokalny bez Ĺ" cieĺźki jest podany jako input) wykonaĹ ' em nastÄ ™ pujÄ ... ce:

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

To pozwala zrobić coś użytecznego, na przykład dodać sufiks do nazwy podstawowej pliku wejściowego jako:

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
Author: Bill Gale,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-09 00:05:19

Możesz użyć

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

Aby uzyskać nazwę pliku i

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

Aby uzyskać rozszerzenie.

Przypadek testowy:

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
Author: Vytenis Bivainis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-22 20:24:16

Oto algorytm, którego użyłem do znalezienia nazwy i rozszerzenia pliku, gdy napisałem skrypt Bash, aby nazwy były unikalne, gdy nazwy są skonfliktowane w odniesieniu do obudowy.

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

Próba.

$ 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=
$ 

FYI: kompletny program do transliteracji i więcej przypadków testowych można znaleźć tutaj: https://www.dropbox.com/s/4c6m0f2e28a1vxf/avoid-clashes-code.zip?dl=0

 0
Author: historystamp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-21 23:25:33