Quero obter o nome do arquivo (sem extensão) e a extensão separadamente.
A melhor solução que encontrei até agora é:
NAME=`echo "$FILE" | cut -d'.' -f1`
EXTENSION=`echo "$FILE" | cut -d'.' -f2`
Isso está errado porque não funciona se o nome do arquivo contiver vários .
caracteres. Se, digamos, eu tiver a.b.js
, ele considerará a
e b.js
, em vez de a.b
ejs
.
Isso pode ser feito facilmente em Python com
file, ext = os.path.splitext(path)
mas prefiro não acionar um intérprete Python apenas para isso, se possível.
Alguma ideia melhor?
extension="{$filename##*.}"
como fiz por um tempo! Mova o$
lado de fora do curlys: Direita:extension="${filename##*.}"
os.path.splitext
como acima, em vez ...Respostas:
Primeiro, obtenha o nome do arquivo sem o caminho:
Como alternativa, você pode focar no último '/' do caminho em vez do '.' que deve funcionar mesmo se você tiver extensões de arquivo imprevisíveis:
Você pode querer verificar a documentação:
fonte
basename
extension=$([[ "$filename" = *.* ]] && echo ".${filename##*.}" || echo '')
. Observe que, se uma extensão estiver presente, ela será retornada, incluindo a inicial.
, por exemplo.txt
,.Para mais detalhes, consulte expansão de parâmetros do shell no manual do Bash.
fonte
dinosaurs.in.tar
e você gzipped-lo paradinosaurs.in.tar.gz
:)x.tar.gz
a extensão de no égz
e o nome do arquivox.tar
é isso. Não existem extensões duplas. Eu tenho certeza que o boost :: filesystem lida dessa maneira. (caminho dividido, change_extension ...) e seu comportamento é baseado em python, se não me engano.Normalmente você já conhece a extensão, então pode usar:
por exemplo:
e chegamos
fonte
basename
é bastante revelador, tipo senhor / senhora :).zip
ou.ZIP
. Existe uma maneira de você fazer algo assimbasename $file {.zip,.ZIP}
?Você pode usar a mágica da expansão de parâmetros POSIX:
Há uma ressalva em que, se o seu nome de arquivo tiver o formato
./somefile.tar.gz
,echo ${FILENAME%%.*}
removeria avidamente a correspondência mais longa com a.
string vazia.(Você pode contornar isso com uma variável temporária:
)
Este site explica mais.
fonte
cut
não possui--complement
esed
não possui-r
.Isso não parece funcionar se o arquivo não tiver extensão ou nome de arquivo. Aqui está o que estou usando; ele usa apenas built-in e lida com mais (mas não todos) nomes de arquivos patológicos.
E aqui estão alguns casos de teste:
fonte
dir="${fullpath:0:${#fullpath} - ${#filename}}"
eu já vi muitas vezesdir="${fullpath%$filename}"
. É mais simples de escrever. Não tenho certeza se existe alguma diferença de velocidade real ou truques.which bash
->/bin/bash
; talvez seja a sua distro?Você pode usar
basename
.Exemplo:
Você precisa fornecer nome base com a extensão que deve ser removido, no entanto, se você está sempre executando
tar
com-z
então você sabe a extensão será.tar.gz
.Isso deve fazer o que você deseja:
fonte
cd $(basename $1 .tar.gz)
funciona para arquivos .gz. Mas em questão ele mencionouArchive files have several extensions: tar.gz, tat.xz, tar.bz2
funciona bem, então você pode simplesmente usar:
Os comandos, a propósito, funcionam da seguinte maneira.
O comando
NAME
substitui um"."
caractere seguido por qualquer número de não-"."
caracteres até o final da linha, sem nada (isto é, remove tudo do final"."
ao final da linha, inclusive). Esta é basicamente uma substituição não gananciosa usando truques de expressões regulares.O comando
EXTENSION
substitutos substitui um número qualquer de caracteres seguido por um"."
caractere no início da linha, sem nada (isto é, remove tudo do início da linha até o ponto final, inclusive). Esta é uma substituição gananciosa, que é a ação padrão.fonte
sed 's,\.[^\.]*$,,'
para nome esed 's,.*\.,., ;t ;g'
extensão (usa os comandos atípicostest
eget
, junto com osubstitute
comando típico ).Mellen escreve em um comentário em um post do blog:
Usando o Bash, também é
${file%.*}
possível obter o nome do arquivo sem a extensão e${file##*.}
obter a extensão sozinha. Isso é,Saídas:
fonte
Não há necessidade de se preocupar com
awk
oused
ou mesmoperl
para esta tarefa simples. Existe umaos.path.splitext()
solução compatível com Bash puro que usa apenas expansões de parâmetros.Implementação de referência
Documentação de
os.path.splitext(path)
:Código Python:
Implementação do Bash
Honrando os principais períodos
Ignorando períodos iniciais
Testes
Aqui estão casos de teste para a implementação de períodos iniciais Ignorando , que devem corresponder à implementação de referência do Python em cada entrada.
Resultado dos testes
Todos os testes foram aprovados.
fonte
text.tar.gz
deve sertext
e extensão ser.tar.gz
os.path.splitext
em Python. Se essa implementação é sensata para possíveis contribuições controversas é outro tópico."$root"
)? O que poderia acontecer se eles fossem omitidos? (Não consegui encontrar nenhuma documentação sobre o assunto.) Além disso, como isso lida com nomes de arquivos com*
ou?
neles?*
e?
não são especiais. Então as duas partes da minha pergunta se respondem. Estou certo de que isso não está documentado? Ou isso deve ser entendido pelo fato de que as aspas desativam a expansão glob em geral?root="${path#?}";root="${path::1}${root%.*}"
- proceda da mesma forma para extrair a extensão.Você pode usar o
cut
comando para remover as duas últimas extensões (a".tar.gz"
parte):Como observado por Clayton Hughes em um comentário, isso não funcionará no exemplo real da pergunta. Portanto, como alternativa, proponho o uso de
sed
expressões regulares estendidas, assim:Ele funciona removendo as duas últimas extensões (alfanuméricas) incondicionalmente.
[Atualizado novamente após comentário de Anders Lindahl]
fonte
$
para verificar se a extensão correspondente está no final do nome do arquivo. Caso contrário, um nome de arquivo como essei.like.tar.gz.files.tar.bz2
pode produzir um resultado inesperado.sed
ordem da cadeia. Mesmo com$
no final um nome de arquivo comompc-1.0.1.tar.bz2.tar.gz
o remove ambos.tar.gz
e depois.tar.bz2
.Aqui estão algumas sugestões alternativas (principalmente em
awk
), incluindo alguns casos de uso avançados, como extrair números de versão para pacotes de software.Todos os casos de uso estão usando o caminho completo original como entrada, sem depender dos resultados intermediários.
fonte
A resposta aceita funciona bem no típico casos , mas não em ponta casos , a saber:
extension=${filename##*.}
retorna o nome do arquivo de entrada em vez de uma sequência vazia.extension=${filename##*.}
não inclui o inicial.
, contrário à convenção..
não funcionaria para nomes de arquivos sem sufixo.filename="${filename%.*}"
será a sequência vazia, se o nome do arquivo de entrada começar com.
e não contiver mais.
caracteres (por exemplo,.bash_profile
) - ao contrário da convenção.---------
Assim, a complexidade de uma solução robusta que cobre todos os casos extremos exige uma função - veja sua definição abaixo; ele pode retornar todos os componentes de um caminho .
Chamada de exemplo:
Observe que os argumentos após o caminho de entrada são escolhidos livremente, nomes de variáveis posicionais .
Para ignorar variáveis que não são de interesse anteriores a essas, especifique
_
(para usar a variável de descarte$_
) ou''
; por exemplo, para extrair apenas a raiz e a extensão do nome do arquivo, usesplitPath '/etc/bash.bashrc' _ _ fnameroot extension
.Código de teste que exerce a função:
Saída esperada - observe os casos extremos:
.
( não considerado o início do sufixo)/
(à direita/
é ignorado).
é retornado como o caminho pai).
token com mais de prefixo (apenas o último é considerado o sufixo):fonte
A solução mais pequena e mais simples (em linha única) é:
fonte
echo
. Em geral,echo $(command)
é melhor escrever simplesmente acommand
menos que você exija especificamente que o shell execute a tokenização de espaço em branco e a expansão de curinga na saídacommand
antes de exibir o resultado. Quiz: qual é o resultadoecho $(echo '*')
(e se é isso que você realmente quer, você realmente quer apenasecho *
).echo
comando. Eu apenas o usei para demonstrar o resultadofoo
que está aparecendo na 3ª linha como resultado da 2ª linha.basename "${file%.*}"
faria o mesmo; você está usando uma substituição de comando para capturar sua saída, apenas paraecho
a mesma saída imediatamente. (Sem citar, o resultado é nominalmente diferente; mas isso é pouco relevante, muito menos uma característica, aqui.)basename "$file" .txt
Evita também a complexidade da substituição de parâmetros.Eu acho que se você só precisa do nome do arquivo, pode tentar o seguinte:
E isso é tudo = D.
fonte
Você pode forçar o corte para exibir todos os campos e os subsequentes adicionando
-
ao número do campo.Portanto, se FILE estiver
eth0.pcap.gz
, a EXTENSÃO serápcap.gz
Usando a mesma lógica, você também pode buscar o nome do arquivo usando '-' com o seguinte:
Isso funciona mesmo para nomes de arquivos que não têm nenhuma extensão.
fonte
Reconhecimento de arquivo mágico
Além das muitas boas respostas sobre essa questão do Stack Overflow, gostaria de acrescentar:
No Linux e outro unixen, há um comando mágico chamado
file
, que detecta o tipo de arquivo analisando alguns primeiros bytes de arquivo. Esta é uma ferramenta muito antiga, usada inicialmente para servidores de impressão (se não for criada para ... não tenho certeza disso).Extensões de padrões podem ser encontradas em
/etc/mime.types
(na minha área de trabalho Debian GNU / Linux. Vejaman file
eman mime.types
. Talvez você precise instalar ofile
utilitário e osmime-support
pacotes):Você pode criar um baterfunção para determinar a extensão correta. Há uma pequena amostra (não perfeita):
Esta função pode definir uma variável Bash que pode ser usada posteriormente:
(Isso é inspirado na resposta correta do @Petesh):
fonte
Ok, então se eu entendi corretamente, o problema aqui é como obter o nome e a extensão completa de um arquivo que possui várias extensões, por exemplo
stuff.tar.gz
,.Isso funciona para mim:
Isso fornecerá o
stuff
nome do arquivo e a.tar.gz
extensão. Funciona para qualquer número de extensões, incluindo 0. Espero que ajude a qualquer pessoa que tenha o mesmo problema =)fonte
os.path.splitext
o que o OP deseja) é('stuff.tar', '.gz')
.Eu uso o seguinte script
fonte
Isso atende a vários pontos e espaços em um nome de arquivo; no entanto, se não houver extensão, ele retornará o próprio nome do arquivo. Fácil de verificar; basta testar se o nome do arquivo e a extensão são os mesmos.
Naturalmente, esse método não funciona para arquivos .tar.gz. No entanto, isso pode ser tratado em um processo de duas etapas. Se a extensão for gz, verifique novamente se há também uma extensão tar.
fonte
Como extrair o nome do arquivo e a extensão no peixe :
Advertências: Divide no último ponto, o que funciona bem para nomes de arquivos com pontos, mas não para extensões com pontos. Veja o exemplo abaixo.
Uso:
Provavelmente existem maneiras melhores de fazer isso. Sinta-se livre para editar minha resposta para melhorá-la.
Se você estiver lidando com um conjunto limitado de extensões e conhecer todas elas, tente o seguinte:
Isso não tem a ressalva como o primeiro exemplo, mas você precisa lidar com todos os casos para que seja mais tedioso, dependendo de quantas extensões você pode esperar.
fonte
Aqui está o código com o AWK . Isso pode ser feito de maneira mais simples. Mas eu não sou bom no AWK.
fonte
split()
.awk -F / '{ n=split($2, a, "."); print a[n] }' uses
/ `como o delimitador de nível superior, mas depois divide os segundos campos.
e imprime o último elemento da nova matriz.Basta usar
${parameter%word}
No seu caso:
Se você quiser testá-lo, siga todos os trabalhos a seguir e remova a extensão:
fonte
=
placas.Com base na resposta Petesh , se apenas o nome do arquivo for necessário, o caminho e a extensão poderão ser removidos em uma única linha,
fonte
filename="$(basename "${fullname%.*}")"
basename
é opcional, mas especifica a extensão a ser removida. A substituição ainda pode ser útil, mas talvezbasename
não seja, uma vez que você pode realmente executar todas essas substituições com os componentes internos do shell.Baseada em grande parte nos excelentes e úteis bashismos aleatórios e úteis do @ mklement0 - além de outras respostas para essas / outras perguntas / "essa maldita Internet" ... Eu envolvi tudo isso de uma forma um pouco mais compreensível, função reutilizável para o meu (ou o seu)
.bash_profile
que cuida do que (eu considero) deve ser uma versão mais robusta dodirname
/basename
/ o que você tem ..Exemplos de uso ...
fonte
$IFS
nada (e, se estivesse, poderia usarlocal
para localizar o efeito de defini-lo). - Melhor usarlocal
variáveis. - Sua mensagem de erro deve ser enviada parastderr
, nãostdout
(uso1>&2
), e você deve retornar um código de saída diferente de zero. - Melhor renomearfullname
parabasename
(o primeiro sugere um caminho com componentes dir). -name
acrescenta incondicionalmente a.
(ponto final), mesmo que o original não tivesse nenhum. Você pode simplesmente usar obasename
utilitário, mas observe que ele ignora uma finalização/
.Uma resposta simples:
Para expandir a resposta das variáveis POSIX , observe que você pode fazer padrões mais interessantes. Portanto, para o caso detalhado aqui, você pode simplesmente fazer o seguinte:
Isso eliminará a última ocorrência de .tar. <algo> .
De maneira mais geral, se você deseja remover a última ocorrência de. <algo> . <algo-algo> então
deve funcionar bem.
O link da resposta acima parece estar morto. Aqui está uma ótima explicação de várias manipulações de string que você pode fazer diretamente no Bash, a partir do TLDP .
fonte
Se você também deseja permitir extensões vazias , este é o mais curto possível:
1a linha explicada: Corresponde a PATH.EXT ou ANYTHING e substitui-o por EXT. Se QUALQUER COISA corresponder, o grupo ext não será capturado.
fonte
Este é o único que funcionou para mim:
Isso também pode ser usado na interpolação de strings, mas, infelizmente, você deve definir com
base
antecedência.fonte
Aqui está o algoritmo que eu usei para encontrar o nome e a extensão de um arquivo quando escrevi um script Bash para tornar os nomes exclusivos quando os nomes conflitavam em relação à maiúsculas e minúsculas.
O teste executado.
FYI: O programa completo de transliteração e mais casos de teste podem ser encontrados aqui: https://www.dropbox.com/s/4c6m0f2e28a1vxf/avoid-clashes-code.zip?dl=0
fonte
extension=$([[ "$theFileName" == *.* ]] && echo ".${theFileName##*.}" || echo '')
Usando o arquivo de exemplo
/Users/Jonathan/Scripts/bash/MyScript.sh
, este código:resultará em
${ME}
serMyScript
e${MY_EXT}
ser.sh
:Roteiro:
Alguns testes:
fonte
basename
é, talvez, um exagero.A partir das respostas acima, o menor delineador para imitar o Python
presumindo que seu arquivo realmente tem uma extensão, é
fonte
EXT
para que se tratem de tartarugas. (Além disso, você deve evitar todas as letras maiúsculas nos nomes de variáveis particulares; elas são reservadas para variáveis do sistema.) #