Como faço para remover o sufixo de arquivo e parte do caminho a partir de uma seqüência de caminho em Bash?

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

  •  02-07-2019
  •  | 
  •  

Pergunta

Dado um caminho de arquivo string como /foo/fizzbuzz.bar, como eu ia usar bash para extrair apenas a parte fizzbuzz da referida corda?

Foi útil?

Solução

Aqui está como fazê-lo com os operadores # e% em Bash.

$ x="/foo/fizzbuzz.bar"
$ y=${x%.bar}
$ echo ${y##*/}
fizzbuzz

${x%.bar} também poderia ser ${x%.*} a tudo retirar após um ponto ou ${x%%.*} para remover tudo após o primeiro ponto.

Exemplo:

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

A documentação pode ser encontrada no Bash manual. Procure seção ${parameter%word} e parte correspondente ${parameter%%word} final.

Outras dicas

olhada no comando basename:

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

festa Pure, feito em duas operações distintas:

  1. Retirar o caminho de um caminho-string:

    path=/foo/bar/bim/baz/file.gif
    
    file=${path##*/}  
    #$file is now 'file.gif'
    
  2. Remover a extensão de um caminho-string:

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

Usando basename eu usei o seguinte para conseguir isso:

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

    # Do things with $fname
done;

Esta não requer um conhecimento a priori da extensão do arquivo e funciona mesmo quando você tem um nome de arquivo que tem pontos em que o nome do arquivo (em frente a sua extensão); ele exige a basename programa, porém, mas isso faz parte dos coreutils GNU assim que deve ser fornecido com qualquer distro.

forma festa Pure:

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

Esta funcionalidade é explicada em man bash em "Parâmetro de Expansão". formas não festança abundam: awk, perl, sed e assim por diante

.

EDIT: Funciona com pontos no arquivo sufixos e não precisa saber o sufixo (extensão), mas não trabalho com pontos no nome em si.

As funções basename e DirName são o que você está depois:

mystring=/foo/fizzbuzz.bar
echo basename: $(basename "${mystring}")
echo basename + remove .bar: $(basename "${mystring}" .bar)
echo dirname: $(dirname "${mystring}")

Tem de saída:

basename: fizzbuzz.bar
basename + remove .bar: fizzbuzz
dirname: /foo
perl -pe 's/\..*$//;s{^.*/}{}'

Usando basename assume que você sabe o que a extensão do arquivo é, não é?

E eu acredito que as várias sugestões de expressões regulares não lidar com um nome de arquivo que contém mais do que uma ""

A seguir parece lidar com pontos duplos. Oh, e nomes de arquivos que contêm um "/"-se (apenas por diversão)

Para parafrasear Pascal, "Desculpe este script é tão longa. Eu não tinha tempo para torná-lo mais curto"


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

Se você não pode usar basename como sugerido em outros lugares, você sempre pode usar sed. Aqui está um (feio) exemplo. Ele não é o maior, mas funciona extraindo a corda queria e substituindo a entrada com a corda queria.

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

O que você vai chegar a saída

fizzbuzz

Além do POSIX conformant sintaxe usado em esta resposta ,

basename string [suffix]

como

basename /foo/fizzbuzz.bar .bar

GNU basename suporta outra sintaxe:

basename -s .bar /foo/fizzbuzz.bar

com o mesmo resultado. A diferença e vantagem é que -s implica -a, que suporta vários argumentos:

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

Este mesmo nome de arquivo pode ser feita em segurança, separando-o da saída com NUL bytes utilizando a opção -z, por exemplo para estes ficheiros que contêm espaços vazios, novas linhas e caracteres glob (citado por ls):

$ ls has*
'has'$'\n''newline.bar'  'has space.bar'  'has*.bar'

Leitura em uma matriz:

$ readarray -d $'\0' arr < <(basename -zs .bar has*)
$ declare -p arr
declare -a arr=([0]=$'has\nnewline' [1]="has space" [2]="has*")

readarray -d requer Bash 4.4 ou mais recente. Para versões mais antigas, temos de loop:

while IFS= read -r -d '' fname; do arr+=("$fname"); done < <(basename -zs .bar has*)

Cuidado com a solução perl sugeriu:. Remove qualquer coisa após o primeiro ponto

$ echo some.file.with.dots | perl -pe 's/\..*$//;s{^.*/}{}'
some

Se você quiser fazê-lo com perl, isso funciona:

$ echo some.file.with.dots | perl -pe 's/(.*)\..*$/$1/;s{^.*/}{}'
some.file.with

Mas, se você estiver usando o Bash, as soluções com y=${x%.*} (ou basename "$x" .ext se você sabe a extensão) são muito mais simples.

O basename faz isso, remove o caminho. Ele também irá remover o sufixo se deu e se ele corresponde ao sufixo do arquivo, mas você precisa saber o sufixo para dar ao comando. Caso contrário, você pode usar mv e descobrir o que o novo nome deve ser alguma outra forma.

Combinando a resposta mais votadas com a resposta segunda-rated-top para obter o nome do arquivo sem o caminho completo:

$ x="/foo/fizzbuzz.bar.quux"
$ y=(`basename ${x%%.*}`)
$ echo $y
fizzbuzz
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top