Извлечь имя файла и расширение в Bash - PullRequest
1849 голосов
/ 08 июня 2009

Я хочу получить имя файла (без расширения) и расширение отдельно.

Лучшее решение, которое я нашел на данный момент:

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

Это неправильно, потому что не работает, если имя файла содержит несколько . символов. Если, скажем, у меня есть a.b.js, он будет рассматривать a и b.js вместо a.b и js.

Это легко сделать на Python с помощью

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

но я бы предпочел не запускать интерпретатор Python только для этого, если это возможно.

Есть идеи получше?

Ответы [ 37 ]

0 голосов
/ 09 августа 2013

Чтобы сделать dir более полезным (в случае, если в качестве входных данных указан локальный файл без пути), я сделал следующее:

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

Это позволяет вам сделать что-то полезное, например добавить суффикс к базовому имени входного файла как:

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 голосов
/ 13 октября 2016

Вот решение sed, которое извлекает компоненты пути в различных формах и может обрабатывать большинство крайних случаев:

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

Вот как это работает:

sed анализирует входной путь и печатает следующие компоненты пути по порядку в отдельных строках:

  • путь к каталогу с символом косой черты
  • путь к каталогу без завершающего слеша
  • имя файла с расширением
  • имя файла без расширения
  • расширение файла с символом начальной точки
  • расширение файла без символа начальной точки

tr преобразует вывод sed в разделительную символьную строку вышеуказанных компонентов пути.

read использует символ разделителя в качестве разделителя полей (IFS="$separatorChar") и присваивает каждый из компонентов пути соответствующей переменной.

Вот как работает конструкция sed:

  • s/^[[:space:]]+// и s/[[:space:]]+$// убрать все начальные и / или конечные пробельные символы
  • t l1 и :l1 обновляет функцию t для следующей функции s
  • s/^([^/]|$)// и t проверяет недопустимый входной путь (путь, который не начинается с косой черты), в этом случае все выходные строки остаются пустыми и выходит из команды sed
  • s/[/]+$// убирает любые косые черты
  • t l2 и :l2 обновляет функцию t для следующей функции s
  • s/^$/filesystem\/\\[newline]filesystem/p и t проверяет особый случай, когда входной путь состоит из корневого каталога / , и в этом случае он печатает filesystem / и filesystem для dirPathWithSlash и dirPath строк вывода, оставляя все остальные строки вывода пустыми и выходит из sed команда
  • h сохраняет входной путь в области удержания
  • s/^(.*)([/])([^/]+)$/\1\2\\[newline]\1\\[newline]\3/p печатает dirPathWithSlash , dirPath и fileNameWithExt строки вывода
  • g извлекает входной путь из пространства удержания
  • t l3 и :l3 обновляет функцию t для следующей s функции
  • s/^.*\[/]([^/]+)([.])([a-zA-Z0-9]+)$/\1\\[newline]\2\3\\[newline]\3/p и t печатает fileName , fileExtWithDot и fileExt строки вывода для случая, когда существует расширение файла (предполагается, что оно состоит только из буквенно-цифровых символов), затем завершает команду sed
  • s/^.*\[/](.+)$/\1/p печатает имя файла , но не fileExtWithDot и fileExt строки вывода для случая, когда расширение файла имеет значение не существует, затем выходит из команды sed.
0 голосов
/ 23 мая 2014

Вы можете использовать

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

чтобы получить имя файла и

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

чтобы получить расширение.

Контрольный пример:

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 голосов
/ 01 июля 2011

Вы также можете использовать цикл for и tr, чтобы извлечь имя файла из пути ...

for x in `echo $path | tr "/" " "`; do filename=$x; done

tr заменяет все разделители " / " в пути пробелами, создавая список строк, и цикл for просматривает их, оставляя последний в переменной filename.

0 голосов
/ 22 октября 2014

Вот алгоритм, который я использовал для нахождения имени и расширения файла, когда писал сценарий Bash, чтобы сделать имена уникальными, когда имена конфликтуют по отношению к регистру.

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

Тестовый прогон.

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

К вашему сведению: полную программу транслитерации и другие тестовые примеры можно найти здесь: https://www.dropbox.com/s/4c6m0f2e28a1vxf/avoid-clashes-code.zip?dl=0

0 голосов
/ 08 июня 2019

Это единственный, который работал на меня:

path='folder/other_folder/file.js'

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

>> file

Это также можно использовать для интерполяции строк, но, к сожалению, вам нужно заранее установить base.

0 голосов
/ 05 февраля 2013

Возможно, в tar есть возможность сделать это; ты проверил человека? В противном случае вы можете использовать расширение строки Bash :

test="mpc-1.0.1.tar.gz"
noExt="${test/.tar.gz/}" # Remove the string '.tar.gz'
echo $noExt
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...