Извлечь базовое имя файла без пути и расширения в bash
Указанные имена файлов, такие как:
/the/path/foo.txt
bar.txt
Я надеюсь получить:
foo
bar
Почему это не работает?
#!/bin/bash
fullfile=$1
fname=$(basename $fullfile)
fbname=${fname%.*}
echo $fbname
Какой правильный способ сделать это?
Ответы
Ответ 1
Вам не нужно вызывать внешнюю команду basename
. Вместо этого вы можете использовать следующие команды:
$ s=/the/path/foo.txt
$ echo ${s##*/}
foo.txt
$ s=${s##*/}
$ echo ${s%.txt}
foo
$ echo ${s%.*}
foo
Обратите внимание, что это решение должно работать во всех недавних (post 2004) POSIX-совместимых оболочках (например, bash
, dash
, ksh
и т.д.).
Источник: Командный язык оболочки 2.6.2 Расширение параметров
Подробнее о bash Строковые манипуляции: http://tldp.org/LDP/LG/issue18/bash.html
Ответ 2
Команда basename имеет два разных вызова; в одном вы указываете только путь, и в этом случае он дает вам последний компонент, в то время как в другом вы также указываете суффикс, который он удалит. Таким образом, вы можете упростить код примера, используя второй вызов basename. Кроме того, будьте осторожны, чтобы правильно процитировать вещи:
fbname=$(basename "$1" .txt)
echo "$fbname"
Ответ 3
Комбинация basename и cut работает отлично, даже в случае двойного окончания, например .tar.gz
:
fbname=$(basename "$fullfile" | cut -d. -f1)
Было бы интересно, если это решение нуждается в меньшей арифметической мощности, чем Bash Расширение параметров.
Ответ 4
Чистый bash
, без basename
, без basename
переменных. Установите строку и echo
:
s=/the/path/foo.txt
echo ${s//+(*\/|.*)}
Выход:
foo
Примечание: опция bash
extglob должна быть включена, (в Ubuntu она включена по умолчанию), если нет, выполните:
shopt -s extglob
Пройдя по ${s//+(*\/|.*)}
:
-
${s
- начать с $ s. -
//
подставляем каждый экземпляр шаблона. -
+(
соответствует одному или нескольким спискам шаблонов в круглых скобках (т.е. до позиции № 7 ниже). - 1-й шаблон:
*\/
соответствует чему-либо перед буквенным символом " /
". - разделитель шаблонов
|
который в этом случае действует как логическое ИЛИ. - 2-й шаблон:.
.*
Соответствует чему-либо после литерала " .
", bash
есть в bash
" .
" - это просто символ точки, а не точка регулярного выражения. -
)
конец списка шаблонов. -
}
расширение параметра end - поскольку нет /
(что должно предшествовать подстановке строки), сопоставленные шаблоны заменяются ничем; это удаляет совпадения.
Соответствующий man bash
background:
- замена шаблона:
${parameter/pattern/string}
Pattern substitution. The pattern is expanded to produce a pat‐
tern just as in pathname expansion. Parameter is expanded and
the longest match of pattern against its value is replaced with
string. If pattern begins with /, all matches of pattern are
replaced with string. Normally only the first match is
replaced. If pattern begins with #, it must match at the begin‐
ning of the expanded value of parameter. If pattern begins with
%, it must match at the end of the expanded value of parameter.
If string is null, matches of pattern are deleted and the / fol‐
lowing pattern may be omitted. If parameter is @ or *, the sub‐
stitution operation is applied to each positional parameter in
turn, and the expansion is the resultant list. If parameter is
an array variable subscripted with @ or *, the substitution
operation is applied to each member of the array in turn, and
the expansion is the resultant list.
- расширенное сопоставление с образцом:
If the extglob shell option is enabled using the shopt builtin, several
extended pattern matching operators are recognized. In the following
description, a pattern-list is a list of one or more patterns separated
by a |. Composite patterns may be formed using one or more of the fol‐
lowing sub-patterns:
?(pattern-list)
Matches zero or one occurrence of the given patterns
*(pattern-list)
Matches zero or more occurrences of the given patterns
+(pattern-list)
Matches one or more occurrences of the given patterns
@(pattern-list)
Matches one of the given patterns
!(pattern-list)
Matches anything except one of the given patterns
Ответ 5
Здесь есть oneliners:
-
$(basename ${s%.*})
-
$(basename ${s} .${s##*.})
Мне это нужно, так же, как попросили bongbang и w4etwetewtwet.
Ответ 6
Вот еще один (более сложный) способ получения имени файла или расширения, сначала используйте команду rev
для инвертирования пути к файлу, вырезанного из первого .
, а затем снова инвертируйте путь к файлу, например:
filename=`rev <<< "$1" | cut -d"." -f2- | rev`
fileext=`rev <<< "$1" | cut -d"." -f1 | rev`
Ответ 7
Если вы хотите хорошо играть с файловыми путями Windows (под Cygwin), вы также можете попробовать следующее:
fname=${fullfile##*[/|\\]}
Это будет учитывать разделители обратной косой черты при использовании BaSH в Windows.
Ответ 8
Просто альтернатива, которую я придумал, чтобы извлечь расширение, используя сообщения в этом потоке с моей собственной небольшой базой знаний, которая была мне более знакома.
ext="$(rev <<< "$(cut -f "1" -d "." <<< "$(rev <<< "file.docx")")")"
Примечание. Просьба сообщить о моем использовании котировок; это сработало для меня, но я мог бы что-то упустить при их правильном использовании (я, вероятно, использую слишком много).
Ответ 9
Используйте команду basename. Его manpage здесь: http://unixhelp.ed.ac.uk/CGI/man-cgi?basename