Учитывая путь к строковому файлу, например /foo/fizzbuzz.bar
, как бы я использовал bash для извлечения только fizzbuzz
части указанной строки?
Учитывая путь к строковому файлу, например /foo/fizzbuzz.bar
, как бы я использовал bash для извлечения только fizzbuzz
части указанной строки?
Ответы:
Вот как это сделать с помощью операторов # и% в Bash.
$ x="/foo/fizzbuzz.bar"
$ y=${x%.bar}
$ echo ${y##*/}
fizzbuzz
${x%.bar}
также может быть ${x%.*}
удалить все после точки или ${x%%.*}
удалить все после первой точки.
Пример:
$ x="/foo/fizzbuzz.bar.quux"
$ y=${x%.*}
$ echo $y
/foo/fizzbuzz.bar
$ y=${x%%.*}
$ echo $y
/foo/fizzbuzz
Документацию можно найти в руководстве по Bash . Посмотрите ${parameter%word}
и ${parameter%%word}
раздел соответствия задней части.
посмотрите на команду basename:
NAME="$(basename /foo/fizzbuzz.bar .bar)"
Используя basename, я использовал следующее для достижения этой цели:
for file in *; do
ext=${file##*.}
fname=`basename $file $ext`
# Do things with $fname
done;
Это не требует априорных знаний о расширении файла и работает, даже если у вас есть имя файла с точками в имени файла (перед его расширением); basename
Хотя для этого требуется программа , но это часть ядра GNU Coreutils, поэтому она должна поставляться с любым дистрибутивом.
fname=`basename $file .$ext`
Pure Bash путь:
~$ x="/foo/bar/fizzbuzz.bar.quux.zoom";
~$ y=${x/\/*\//};
~$ echo ${y/.*/};
fizzbuzz
Эта функциональность описана на man bash в разделе «Расширение параметров». Существует множество способов, не связанных с bash: awk, perl, sed и так далее.
РЕДАКТИРОВАТЬ: работает с точками в суффиксах файлов и не нужно знать суффикс (расширение), но не работает с точками в самом имени .
Функции basename и dirname - это то, что вам нужно:
mystring=/foo/fizzbuzz.bar
echo basename: $(basename "${mystring}")
echo basename + remove .bar: $(basename "${mystring}" .bar)
echo dirname: $(dirname "${mystring}")
Имеет выход:
basename: fizzbuzz.bar
basename + remove .bar: fizzbuzz
dirname: /foo
mystring=$1
вместо текущего постоянного значения (которое будет подавлять несколько предупреждений, будучи уверенным, что они не содержат пробелы / символы глобуса / и т. Д.), И решите проблемы, с которыми оно связано находки?
echo "basename: $(basename "$mystring")"
- таким образом, если mystring='/foo/*'
вы не получите *
замену со списком файлов в текущем каталоге после basename
завершения.
Использование basename
предполагает, что вы знаете, что такое расширение файла, не так ли?
И я считаю, что различные предложения по регулярным выражениям не справляются с именем файла, содержащим более одного "."
Следующее, кажется, справляется с двойными точками. Да, и имена файлов, которые содержат "/" сами (только для ударов)
Перефразируя Паскаля: «Извините, этот скрипт такой длинный. У меня не было времени сделать его короче»
#!/usr/bin/perl
$fullname = $ARGV[0];
($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
print $basename . "\n";
В дополнение к согласованному синтаксису POSIX, используемому в этом ответе ,
basename string [suffix]
как в
basename /foo/fizzbuzz.bar .bar
GNUbasename
поддерживает другой синтаксис:
basename -s .bar /foo/fizzbuzz.bar
с тем же результатом. Разница и преимущество состоит в том, что -s
означает -a
, что поддерживает несколько аргументов:
$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar
Это даже можно сделать безопасным для имени файла, разделив вывод NUL-байтами, используя -z
опцию, например, для этих файлов, содержащих пробелы, символы новой строки и символы глобуса (цитируются ls
):
$ ls has*
'has'$'\n''newline.bar' 'has space.bar' 'has*.bar'
Чтение в массив:
$ readarray -d $'\0' arr < <(basename -zs .bar has*)
$ declare -p arr
declare -a arr=([0]=$'has\nnewline' [1]="has space" [2]="has*")
readarray -d
требуется Bash 4.4 или новее. Для более старых версий мы должны выполнить цикл:
while IFS= read -r -d '' fname; do arr+=("$fname"); done < <(basename -zs .bar has*)
Если вы не можете использовать базовое имя, как предлагается в других сообщениях, вы всегда можете использовать sed. Вот (ужасный) пример. Он не самый лучший, но он работает, извлекая искомую строку и заменяя ввод желаемой строкой.
echo '/foo/fizzbuzz.bar' | sed 's|.*\/\([^\.]*\)\(\..*\)$|\1|g'
Что даст вам выход
FizzBuzz
Остерегайтесь предложенного решения perl: оно удаляет все, что находится после первой точки.
$ echo some.file.with.dots | perl -pe 's/\..*$//;s{^.*/}{}'
some
Если вы хотите сделать это с Perl, это работает:
$ echo some.file.with.dots | perl -pe 's/(.*)\..*$/$1/;s{^.*/}{}'
some.file.with
Но если вы используете Bash, решения с y=${x%.*}
(или, basename "$x" .ext
если вы знаете расширение) гораздо проще.
Базовое имя делает это, удаляет путь. Он также удалит суффикс, если он задан, и, если он совпадает с суффиксом файла, но вам необходимо знать суффикс, который нужно дать команде. В противном случае вы можете использовать mv и выяснить, какое новое имя должно быть другим способом.
Объединяя ответ с наивысшим рейтингом и ответ с наивысшим рейтингом, чтобы получить имя файла без полного пути:
$ x="/foo/fizzbuzz.bar.quux"
$ y=(`basename ${x%%.*}`)
$ echo $y
fizzbuzz
${parameter%word}
и${parameter%%word}
в разделе соответствия конечной части.