Как удалить суффикс файла и путь из строки пути в Bash?

Учитывая путь к строковому файлу, например /foo/fizzbuzz.bar, как бы я использовал bash для извлечения только части fizzbuzz из указанной строки?

Ответ 1

Здесь, как это сделать С# и% операторами в 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} раздел соответствия конечной части.

Ответ 2

посмотрите на команду basename:

NAME=$(basename /foo/fizzbuzz.bar .bar)

Ответ 3

Чистый bash, выполненный в двух отдельных операциях:

  • Удалите путь из строки пути:

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

    base=${file%.*}
    #${base} is now 'file'.
    

Ответ 4

Используя базовое имя, я использовал для этого следующее:

for file in *; do
    ext=${file##*.}
    fname=`basename $file $ext`

    # Do things with $fname
done;

Это не требует априорного знания расширения файла и работает даже тогда, когда у вас есть имя файла, в котором есть точки в имени файла (перед его расширением); он действительно требует программы basename, хотя, но это часть GNU coreutils, поэтому он должен поставляться с любым дистрибутивом.

Ответ 5

Чистый bash способ:

~$ x="/foo/bar/fizzbuzz.bar.quux.zoom"; 
~$ y=${x/\/*\//}; 
~$ echo ${y/.*/}; 
fizzbuzz

Эта функция объясняется man bash в разделе "Расширение параметров". Не существует bash способов: awk, perl, sed и т.д.

EDIT: работает с точками в суффиксах файлов и не нуждается в суффиксе (расширении), но не работает с точками в самом имени.

Ответ 6

Функции 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

Ответ 7

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

Ответ 8

Использование basename предполагает, что вы знаете, что такое расширение файла, не так ли?

И я считаю, что различные предложения регулярного выражения не справляются с именем файла, содержащим более одного..

Следующее, похоже, справляется с двойными точками. О, и имена файлов, которые содержат "/" сами (только для ударов)

Перефразируя Pascal, "Извините, этот script так длинный. У меня не было времени, чтобы сделать его короче"


  #!/usr/bin/perl
  $fullname = $ARGV[0];
  ($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
  ($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
  print $basename . "\n";
 

Ответ 9

Если вы не можете использовать basename, как указано в других сообщениях, вы всегда можете использовать sed. Вот пример (уродливый). Это не самый большой, но он работает, извлекая нужную строку и заменяя входную строку требуемой строкой.

echo '/foo/fizzbuzz.bar' | sed 's|.*\/\([^\.]*\)\(\..*\)$|\1|g'

Что вы получите на выходе

FizzBuzz

Ответ 10

В дополнение к согласованному синтаксису POSIX, используемому в этом ответе,

basename string [suffix]

как в

basename /foo/fizzbuzz.bar .bar

basename GNU поддерживает другой синтаксис:

basename -s .bar /foo/fizzbuzz.bar

с тем же результатом. Разница и преимущество в том, что -s подразумевает -a, который поддерживает несколько аргументов:

$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar

Это даже можно сделать именем файла -s afe, разделив вывод с помощью байтов 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*)

Ответ 11

Остерегайтесь предлагаемого решения 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, если вы знаете расширение) намного проще.

Ответ 12

Базовое имя делает это, удаляет путь. Он также удалит суффикс, если он указан, и если он совпадает с суффиксом файла, но вам нужно будет знать суффикс, который нужно передать команде. В противном случае вы можете использовать mv и выяснить, какое новое имя должно быть каким-то другим способом.

Ответ 13

Сочетание ответа с наивысшим рейтингом с ответом второго сверху, чтобы получить имя файла без полного пути:

$ x="/foo/fizzbuzz.bar.quux"
$ y=(`basename ${x%%.*}`)
$ echo $y
fizzbuzz

Ответ 14

Информацию вы можете найти в Bash manual, найдите ${parameter%word} и ${parameter%%word} в разделе соответствия конечной части.