Как удалить суффикс файла и часть пути из строки пути в Bash?
Заданный путь к строковому файлу, такой как /foo/fizzbuzz.bar
Как бы я использовал Bash для извлечения только fizzbuzz
часть указанной строки?
16 ответов
Вот как это сделать с помощью операторов # и% в 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`
Чистый 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, поэтому он должен поставляться с любым дистрибутивом.
Функции 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
Pure Bash путь:
~$ x="/foo/bar/fizzbuzz.bar.quux.zoom";
~$ y=${x/\/*\//};
~$ echo ${y/.*/};
fizzbuzz
Эта функциональность описана на man bash в разделе "Расширение параметров". Существует множество способов, не связанных с bash: awk, perl, sed и так далее.
РЕДАКТИРОВАТЬ: работает с точками в суффиксах файлов и не нужно знать суффикс (расширение), но не работает с точками в самом имени.
С помощью 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
Если вы хотите сохранить только имя файла с расширением и удалить путь к файлу
$ x="myfile/hello/foo/fizzbuzz.bar"
$ echo ${x##*/}
$ fizzbuzz.bar
Объяснение в руководстве Bash см.${parameter##word}
Остерегайтесь предложенного решения 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
Информацию вы можете найти в руководстве Bash, ищите ${parameter%word}
а также ${parameter%%word}
в разделе соответствия задней части.
Вы можете использовать
mv *<PATTERN>.jar "$(basename *<PATTERN>.jar <PATTERN>.jar).jar"
Например: - Я хотел удалить
-SNAPSHOT
из моего имени файла. Для используемой ниже команды
mv *-SNAPSHOT.jar "$(basename *-SNAPSHOT.jar -SNAPSHOT.jar).jar"