Как мне удалить суффикс файла и часть пути из строки пути в Bash?
Вопрос
Задан строковый путь к файлу, такой как /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
Документацию можно найти в Руководство по запуску.Ищите ${parameter%word}
и ${parameter%%word}
завершающая часть, соответствующая разделу.
Другие советы
посмотрите на команду basename:
NAME=$(basename /foo/fizzbuzz.bar .bar)
Чистый bash, выполняемый двумя отдельными операциями:
Удалить путь из строки пути:
path=/foo/bar/bim/baz/file.gif file=${path##*/} #$file is now 'file.gif'
Удалите расширение из строки пути:
base=${file%.*} #${base} is now 'file'.
Используя basename, я использовал следующее для достижения этой цели:
for file in *; do
ext=${file##*.}
fname=`basename $file $ext`
# Do things with $fname
done;
Это не требует априорного знания расширения файла и работает, даже если у вас есть имя файла, в котором есть точки в имени файла (перед его расширением);для этого действительно требуется программа basename
хотя, но это часть GNU coreutils, поэтому она должна поставляться с любым дистрибутивом.
Чистый способ 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
perl -pe 's/\..*$//;s{^.*/}{}'
Используя basename
предполагается, что вы знаете, каково расширение файла, не так ли?
И я считаю, что различные предложения по регулярным выражениям не справляются с именем файла, содержащим более одного "."
Следующее, по-видимому, справляется с двойными точками.О, и имена файлов, которые сами содержат "/" (просто для пущего эффекта)
Перефразируя Паскаля, "Извините, что этот скрипт такой длинный.У меня не было времени сделать это короче".
#!/usr/bin/perl
$fullname = $ARGV[0];
($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
print $basename . "\n";
Если вы не можете использовать basename, как предлагалось в других сообщениях, вы всегда можете использовать sed .Вот (уродливый) пример.Это не самый лучший способ, но он работает путем извлечения нужной строки и замены входных данных нужной строкой.
echo '/foo/fizzbuzz.bar' | sed 's|.*\/\([^\.]*\)\(\..*\)$|\1|g'
Который даст вам результат
шипение
В дополнение к Синтаксис , соответствующий POSIX используется в этот ответ,
basename строка [суффикс]
как в
basename /foo/fizzbuzz.bar .bar
ГНУ basename
поддерживает другой синтаксис:
basename -s .bar /foo/fizzbuzz.bar
с тем же результатом.Разница и преимущество в том, что -s
подразумевает -a
, который поддерживает несколько аргументов:
$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar
Это даже можно сделать безопасным для имени файла, разделив выходные данные нулевыми байтами с помощью -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*)
Остерегайтесь предлагаемого решения на 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