Como posso remover a extensão de um nome de arquivo em um script de shell?

143

O que há de errado com o código a seguir?

name='$filename | cut -f1 -d'.''

Como é, recebo a string literal $filename | cut -f1 -d'.', mas se removo as aspas, não recebo nada. Enquanto isso, digitando

"test.exe" | cut -f1 -d'.'

em uma concha me dá a saída que eu quero test,. Eu já sei $filenameque foi atribuído o valor certo. O que eu quero fazer é atribuir a uma variável o nome do arquivo sem a extensão.

mimicocotopus
fonte
6
basename $filename .exefaria a mesma coisa. Supondo que você sempre saiba qual extensão deseja remover.
Mpe
6
@pepe, você quer dizer basename "$filename" .exe. Caso contrário, os nomes de arquivos com espaços seriam más notícias.
Charles Duffy

Respostas:

113

Você deve usar a sintaxe de substituição de comando$(command) quando desejar executar um comando em script / comando.

Então sua linha seria

name=$(echo "$filename" | cut -f 1 -d '.')

Explicação do código:

  1. echoobter o valor da variável $filenamee enviá-lo para a saída padrão
  2. Em seguida, agarramos a saída e direcionamos para o cutcomando
  3. O cutusará o. como delimitador (também conhecido como separador) para cortar a sequência em segmentos e -fselecionamos qual segmento queremos ter na saída
  4. Em seguida, a $()substituição do comando obterá a saída e retornará seu valor
  5. O valor retornado será atribuído à variável denominada name

Observe que isso fornece a parte da variável até o primeiro período .:

$ filename=hello.world
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello.hello.hello
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello
$ echo "$filename" | cut -f 1 -d '.'
hello
Rohan
fonte
Obrigado. Também notei que preciso usar o comando echo. nome =echo $filename | cut -f1 -d'.'
mimicocotopus 28/08
17
Backticks são descontinuados pelo POSIX, $()é o preferido.
jordanm
3
Bifurcação e tubulação para obter alguns caracteres é a pior solução imaginável.
Jens
39
O problema com essa resposta é que assume cadeia de entrada tem apenas um ponto ... @chepner abaixo tem uma solução muito melhor ... name = $ {filename% *.}
Scott Stensland
4
Esta resposta é característica dos iniciantes e não deve ser espalhada. Use o mecanismo incorporado como descrito por de chepner resposta
Neric
256

Você também pode usar a expansão de parâmetros:

$ filename=foo.txt
$ echo "${filename%.*}"
foo
chepner
fonte
6
aqui a explicação do comando: gnu.org/software/bash/manual/html_node/...
Carlos Robles
1
E aqui eu estava prestes a usar echo -n "This.File.Has.Periods.In.It.txt" | awk -F. '{$NF=""; print $0}' | tr ' ' '.' | rev | cut -c 2- | rev. Obrigado.
usar o seguinte comando
1
Isso funciona com arquivos com várias extensões, como image.png.gz?
precisa saber é o seguinte
11
%.*removerá apenas a última extensão; se você deseja remover todas as extensões, use %%.*.
Chepner # 26/18
1
Isso parece funcionar muito melhor do que a resposta aceita. Ele remove a última extensão apenas se o arquivo tiver mais de um ponto, enquanto o corte remove tudo após o primeiro ponto.
Dale Anderson
117

Se você conhece a extensão, pode usar o nome da base

$ basename /home/jsmith/base.wiki .wiki
base
Steven Penny
fonte
Como posso remover just .wikie acabar com /home/jsmith/base?
Gabriel Staples
Atualização: resposta: consulte stackoverflow.com/a/32584935/4561887 . Funciona perfeitamente!
Gabriel Staples
20

Se o seu nome de arquivo contiver um ponto (que não seja o da extensão), use este:

echo $filename | rev | cut -f 2- -d '.' | rev
manish_s
fonte
1
Eu esqueci a rotação do meio, mas uma vez que eu vi, isso foi ótimo!
supremo Pooba 29/09/16
É ainda melhor com uma -sopção dada cut, para que ele retorne uma string vazia sempre que o nome do arquivo não contiver pontos.
Hibou57 27/04
1
Essa deve ser a resposta aceita na minha opinião, pois funciona no caminho com pontos neles, com arquivos ocultos começando com um ponto ou mesmo com arquivos com várias extensões.
Tim Krief 22/06
20
file1=/tmp/main.one.two.sh
t=$(basename "$file1")                        # output is main.one.two.sh
name=$(echo "$file1" | sed -e 's/\.[^.]*$//') # output is /tmp/main.one.two
name=$(echo "$t" | sed -e 's/\.[^.]*$//')     # output is main.one.two

use o que quiser. Aqui, suponho que o último .(ponto) seguido pelo texto seja extensão.

Raghwendra
fonte
6
#!/bin/bash
file=/tmp/foo.bar.gz
echo $file ${file%.*}

saídas:

/tmp/foo.bar.gz /tmp/foo.bar

Observe que apenas a última extensão é removida.

C. Paul Bond
fonte
3

Minha recomendação é usar basename.
É por padrão no Ubuntu, código visualmente simples e lida com a maioria dos casos.

Aqui estão alguns sub-casos para lidar com espaços e multi-pontos / sub-extensões:

pathfile="../space fld/space -file.tar.gz"
echo ${pathfile//+(*\/|.*)}

Geralmente ele se livra da extensão desde o início ., mas falha em nosso ..caminho

echo **"$(basename "${pathfile%.*}")"**  
space -file.tar     # I believe we needed exatly that

Aqui está uma nota importante:

Usei aspas duplas dentro de aspas duplas para lidar com espaços. As aspas simples não serão aprovadas devido ao envio de $. O Bash é incomum e lê "segundo" primeiro "aspas" devido à expansão.

No entanto, você ainda precisa pensar em .hidden_files

hidden="~/.bashrc"
echo "$(basename "${hidden%.*}")"  # will produce "~" !!!  

não é o resultado "" esperado. Para que isso aconteça, use $HOMEou /home/user_path/
porque novamente o bash é "incomum" e não expanda "~" (pesquise bash BitPitfalls)

hidden2="$HOME/.bashrc" ;  echo '$(basename "${pathfile%.*}")'
Alexey K.
fonte
1
#!/bin/bash
filename=program.c
name=$(basename "$filename" .c)
echo "$name"

saídas:

program
booboo
fonte
Como isso é diferente da resposta dada por Steven Penny há 3 anos?
precisa saber é o seguinte
1

Como apontado por Hawker65 no comentário da resposta do chepner, a solução mais votada não cuida de várias extensões (como filename.tar.gz), nem de pontos no restante do caminho (como this.path / with .dots / in.path.name). Uma solução possível é:

a=this.path/with.dots/in.path.name/filename.tar.gz
echo $(dirname $a)/$(basename $a | cut -d. -f1)
MadMage
fonte
Este remove "tar.gz" selecionando caracteres antes da primeira instância de um ponto no nome do arquivo sem contar o caminho. Provavelmente não se quer remover extensões dessa maneira.
Frotz 24/10
0

Dois problemas com seu código:

  1. Você usou um '(tick) em vez de um `(back tick) para cercar os comandos que geram a string que você deseja armazenar na variável.
  2. Você não "ecoou" a variável "$ filename" no canal para o comando "cut".

Eu mudaria seu código para "name =` echo $ filename | cut -f 1 -d '.' `", como mostrado abaixo (novamente, observe as marcações em volta da definição da variável de nome):

$> filename=foo.txt
$> echo $filename
foo.txt
$> name=`echo $filename | cut -f1 -d'.'`
$> echo $name
foo
$> 
FanDeLaU
fonte