Как удалить суффикс файла и часть пути из строки пути в 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, сделанный в двух отдельных операциях:

  1. Удалить путь из строки пути:

    path=/foo/bar/bim/baz/file.gif
    
    file=${path##*/}  
    #$file is now 'file.gif'
    
  2. Удалите расширение из строки пути:

    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

perl -pe 's/\..*$//;s{^.*/}{}'

Если вы хотите сохранить только имя файла с расширением и удалить путь к файлу

      $ 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"
Другие вопросы по тегам