Como faço para remover o sufixo de arquivo e parte do caminho a partir de uma seqüência de caminho em Bash?
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?
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:
-
Retirar o caminho de um caminho-string:
path=/foo/bar/bim/baz/file.gif file=${path##*/} #$file is now 'file.gif'
-
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