Как удалить суффикс файла и часть пути из строки пути в Bash? - PullRequest
349 голосов
/ 24 сентября 2008

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

Ответы [ 13 ]

489 голосов
/ 24 сентября 2008

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

189 голосов
/ 24 сентября 2008

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

NAME=$(basename /foo/fizzbuzz.bar .bar)
33 голосов
/ 06 мая 2014

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

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

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

    base=${file%.*}
    #${base} is now 'file'.
    
15 голосов
/ 11 апреля 2014

Используя basename, я использовал следующее для достижения этой цели:

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

    # Do things with $fname
done;

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

12 голосов
/ 24 сентября 2008

Чистый путь:

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

Эта функция описана в man bash в разделе «Расширение параметров». Существует множество способов, не связанных с bash: awk, perl, sed и т. Д.

РЕДАКТИРОВАТЬ: работает с точками в файле суффиксы и не нужно знать суффикс (расширение), но не не работает с точками в имя сама.

9 голосов
/ 24 сентября 2008

Функции 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
3 голосов
/ 25 октября 2018

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

basename <i>string</i> [<i>suffix</i>]

как в

basename /foo/fizzbuzz.bar .bar

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

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*)
3 голосов
/ 24 сентября 2008

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

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

Что даст вам вывод

FizzBuzz

3 голосов
/ 24 сентября 2008

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

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

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

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


  #!/usr/bin/perl
  $fullname = $ARGV[0];
  ($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
  ($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
  print $basename . "\n";
 
3 голосов
/ 24 сентября 2008
perl -pe 's/\..*$//;s{^.*/}{}'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...