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

StackOverflow https://stackoverflow.com/questions/125281

  •  02-07-2019
  •  | 
  •  

Вопрос

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

  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, поэтому она должна поставляться с любым дистрибутивом.

Чистый способ 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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top