Dado um caminho de arquivo de string, como /foo/fizzbuzz.bar
, como eu usaria o bash para extrair apenas a fizzbuzz
parte da referida string?
Dado um caminho de arquivo de string, como /foo/fizzbuzz.bar
, como eu usaria o bash para extrair apenas a fizzbuzz
parte da referida string?
Respostas:
Veja como fazer isso com os operadores # e% no Bash.
$ x="/foo/fizzbuzz.bar"
$ y=${x%.bar}
$ echo ${y##*/}
fizzbuzz
${x%.bar}
Também pode ser ${x%.*}
remover tudo após um ponto ou ${x%%.*}
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 manual do Bash . Procure ${parameter%word}
e ${parameter%%word}
arraste a seção correspondente da parte.
veja o comando basename:
NAME="$(basename /foo/fizzbuzz.bar .bar)"
Usando o nome de base, usei o seguinte para conseguir isso:
for file in *; do
ext=${file##*.}
fname=`basename $file $ext`
# Do things with $fname
done;
Isso não requer conhecimento a priori da extensão do arquivo e funciona mesmo quando você tem um nome de arquivo com pontos no nome do arquivo (na frente da extensão); embora exija o programa basename
, mas isso faz parte dos coreutils do GNU, portanto deve ser fornecido com qualquer distribuição.
fname=`basename $file .$ext`
Maneira pura da festança:
~$ x="/foo/bar/fizzbuzz.bar.quux.zoom";
~$ y=${x/\/*\//};
~$ echo ${y/.*/};
fizzbuzz
Esta funcionalidade é explicada no man bash em "Expansão de parâmetros". Maneiras não bash abundam: awk, perl, sed e assim por diante.
EDIT: trabalha com pontos nos sufixos do arquivo e não precisa saber o sufixo (extensão), mas não funciona com pontos no próprio nome .
As funções basename e dirname são o que você procura:
mystring=/foo/fizzbuzz.bar
echo basename: $(basename "${mystring}")
echo basename + remove .bar: $(basename "${mystring}" .bar)
echo dirname: $(dirname "${mystring}")
Possui saída:
basename: fizzbuzz.bar
basename + remove .bar: fizzbuzz
dirname: /foo
mystring=$1
o valor constante atual (que suprimirá vários avisos, garantindo que não contenha espaços / caracteres globos / etc) e resolva os problemas encontra?
echo "basename: $(basename "$mystring")"
- dessa forma, se mystring='/foo/*'
você não for *
substituído por uma lista de arquivos no diretório atual após a basename
conclusão.
Usar basename
supõe que você saiba qual é a extensão do arquivo, não é?
E acredito que as várias sugestões de expressões regulares não lidam com um nome de arquivo que contém mais de um "."
O seguinte parece lidar com pontos duplos. Ah, e nomes de arquivos que contenham um "/" (apenas para brincadeiras)
Parafraseando Pascal, "Desculpe, este script é tão longo. Não tive tempo para reduzi-lo"
#!/usr/bin/perl
$fullname = $ARGV[0];
($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
print $basename . "\n";
Além da sintaxe em conformidade com POSIX usada nesta resposta ,
basename string [suffix]
como em
basename /foo/fizzbuzz.bar .bar
O GNUbasename
suporta outra sintaxe:
basename -s .bar /foo/fizzbuzz.bar
com o mesmo resultado. A diferença e vantagem é que isso -s
implica -a
, que suporta vários argumentos:
$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar
Isso pode até tornar o nome do arquivo seguro, separando a saída com bytes NUL usando a -z
opção, por exemplo, para esses arquivos que contêm espaços em branco, novas linhas e caracteres glob (citados por ls
):
$ ls has*
'has'$'\n''newline.bar' 'has space.bar' 'has*.bar'
Lendo 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 o Bash 4.4 ou mais recente. Para versões mais antigas, temos que fazer um loop:
while IFS= read -r -d '' fname; do arr+=("$fname"); done < <(basename -zs .bar has*)
Se você não pode usar o nome da base como sugerido em outras postagens, sempre pode usar o sed. Aqui está um exemplo (feio). Não é o melhor, mas funciona extraindo a sequência desejada e substituindo a entrada pela sequência desejada.
echo '/foo/fizzbuzz.bar' | sed 's|.*\/\([^\.]*\)\(\..*\)$|\1|g'
O que lhe dará a saída
fizzbuzz
Cuidado com a solução perl sugerida: ela remove qualquer coisa após o primeiro ponto.
$ echo some.file.with.dots | perl -pe 's/\..*$//;s{^.*/}{}'
some
Se você quiser fazer isso 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ê conhece a extensão) são muito mais simples.
O nome da base faz isso, remove o caminho. Ele também removerá o sufixo se for fornecido e se corresponder ao sufixo do arquivo, mas você precisará saber o sufixo a ser fornecido ao comando. Caso contrário, você pode usar mv e descobrir qual deve ser o novo nome de outra maneira.
Combinando a resposta com a melhor classificação com a segunda resposta com a melhor classificação para obter o nome do arquivo sem o caminho completo:
$ x="/foo/fizzbuzz.bar.quux"
$ y=(`basename ${x%%.*}`)
$ echo $y
fizzbuzz
${parameter%word}
e${parameter%%word}
na seção correspondente à parte à direita.