Указанные имена файлов, такие как:
/the/path/foo.txt
bar.txt
Я надеюсь получить:
foo
bar
Почему это не работает?
#!/bin/bash
fullfile=$1
fname=$(basename $fullfile)
fbname=${fname%.*}
echo $fbname
Какой правильный способ сделать это?
Указанные имена файлов, такие как:
/the/path/foo.txt
bar.txt
Я надеюсь получить:
foo
bar
Почему это не работает?
#!/bin/bash
fullfile=$1
fname=$(basename $fullfile)
fbname=${fname%.*}
echo $fbname
Какой правильный способ сделать это?
Вам не нужно вызывать внешнюю команду 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
Команда basename имеет два разных вызова; в одном вы указываете только путь, и в этом случае он дает вам последний компонент, в то время как в другом вы также указываете суффикс, который он удалит. Таким образом, вы можете упростить код примера, используя второй вызов basename. Кроме того, будьте осторожны, чтобы правильно процитировать вещи:
fbname=$(basename "$1" .txt) echo "$fbname"
Комбинация basename и cut работает отлично, даже в случае двойного окончания, например .tar.gz
:
fbname=$(basename "$fullfile" | cut -d. -f1)
Было бы интересно, если это решение нуждается в меньшей арифметической мощности, чем Bash Расширение параметров.
Чистый bash
, без basename
, без basename
переменных. Установите строку и echo
:
s=/the/path/foo.txt
echo ${s//+(*\/|.*)}
Выход:
foo
Примечание: опция bash
extglob должна быть включена, (в Ubuntu она включена по умолчанию), если нет, выполните:
shopt -s extglob
Пройдя по ${s//+(*\/|.*)}
:
${s
- начать с $ s.//
подставляем каждый экземпляр шаблона.+(
соответствует одному или нескольким спискам шаблонов в круглых скобках (т.е. до позиции № 7 ниже).*\/
соответствует чему-либо перед буквенным символом " /
".|
который в этом случае действует как логическое ИЛИ..*
Соответствует чему-либо после литерала " .
", 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
Здесь есть oneliners:
$(basename ${s%.*})
$(basename ${s} .${s##*.})
Мне это нужно, так же, как попросили bongbang и w4etwetewtwet.
Вот еще один (более сложный) способ получения имени файла или расширения, сначала используйте команду rev
для инвертирования пути к файлу, вырезанного из первого .
, а затем снова инвертируйте путь к файлу, например:
filename=`rev <<< "$1" | cut -d"." -f2- | rev`
fileext=`rev <<< "$1" | cut -d"." -f1 | rev`
Если вы хотите хорошо играть с файловыми путями Windows (под Cygwin), вы также можете попробовать следующее:
fname=${fullfile##*[/|\\]}
Это будет учитывать разделители обратной косой черты при использовании BaSH в Windows.
Просто альтернатива, которую я придумал, чтобы извлечь расширение, используя сообщения в этом потоке с моей собственной небольшой базой знаний, которая была мне более знакома.
ext="$(rev <<< "$(cut -f "1" -d "." <<< "$(rev <<< "file.docx")")")"
Примечание. Просьба сообщить о моем использовании котировок; это сработало для меня, но я мог бы что-то упустить при их правильном использовании (я, вероятно, использую слишком много).
Используйте команду basename. Его manpage здесь: http://unixhelp.ed.ac.uk/CGI/man-cgi?basename