Como verificar a extensão de um nome de arquivo em um script bash?


170

Estou escrevendo um script de construção noturno no bash.
Tudo está bem e elegante, exceto por um pequeno obstáculo:


#!/bin/bash

for file in "$PATH_TO_SOMEWHERE"; do
      if [ -d $file ]
      then
              # do something directory-ish
      else
              if [ "$file" == "*.txt" ]       #  this is the snag
              then
                     # do something txt-ish
              fi
      fi
done;

Meu problema é determinar a extensão do arquivo e agir em conformidade. Eu sei que o problema está na instrução if, testando um arquivo txt.

Como posso determinar se um arquivo tem um sufixo .txt?


Isso será interrompido se você tiver um arquivo com um espaço em seu nome.
Jfg956

Além da resposta de Paulo, você pode usar $(dirname $PATH_TO_SOMEWHERE)e $(basename $PATH_TO_SOMEWHERE)se dividir em pasta e diretório e fazer algo diretório-ish e arquivo-ish
McPeppr

Respostas:


248

Eu acho que você quer dizer "Os quatro últimos caracteres de $ file são iguais .txt?" Nesse caso, você pode usar o seguinte:

if [ ${file: -4} == ".txt" ]

Observe que o espaço entre file:e -4é necessário, pois o modificador ': -' significa algo diferente.


1
para esse fim, você também pode renomear command.com para command.txt em uma máquina Windows.
9139 hometoast

9
Se você deseja especificar uma desigualdade, lembre-se de incluir suportes extras: if [[$ {file: -4} = ".txt"]!]
Ram Rajamony

4
@RamRajamony Por que é necessário usar [[ao testar a desigualdade?
precisa saber é o seguinte

Eu queria ressaltar que o espaço após o cólon é importante. ${var:-4}não é o mesmo que ${var: -4}; o primeiro (sem espaço) será expandido como '-4' se var estiver desativado; o segundo (com espaço) retornará os últimos 4 caracteres de var.
Pbatey 19/07/19

1
No bash, isso produzirá um erro "[: ==: operador unário esperado", a menos que você coloque aspas na primeira variável. Então, em if [ "${file: -4}" == ".txt" ]vez disso.
Giles B

264

Faço

if [ "$file" == "*.txt" ]

como isso:

if [[ $file == *.txt ]]

Ou seja, colchetes duplos e sem aspas.

O lado direito ==é um padrão de concha. Se você precisar de uma expressão regular, use-o =~então.


15
Eu não sabia disso. Parece ser um caso especial em que o lado direito de == ou! = É expandido como um padrão de shell. Pessoalmente, acho que isso é mais claro do que minha resposta.
21139 Paul Stephenson

20
Eu sou novo no bash e demorei um pouco para descobrir como usar isso em uma ifdeclaração multi-condicional . Estou compartilhando aqui, caso ajude alguém. if [[ ( $file == *.csv ) || ( $file == *.png ) ]]
joelostblom

6
@cheflo que é bom para várias condições em geral. Nesse caso específico, você também pode usar if [[ $file =~ .*\.(csv|png) ]]. É mais curto, mais claro, mais fácil de adicionar extensões adicionais e pode ser facilmente configurável (colocando "csv | png" em uma variável).
jox

4
Você pode colocar aspas duplas no arquivo. if [[ "$file" == *.txt ]]Se o arquivo tiver espaços em seu nome, serão necessárias aspas duplas.
shawnhcorey

Devo usar esta resposta em vez da resposta aceita?
Freedo

26

Você simplesmente não pode ter certeza em um sistema Unix, que um arquivo .txt é realmente um arquivo de texto. Sua melhor aposta é usar "arquivo". Talvez tente usar:

file -ib "$file"

Em seguida, você pode usar uma lista de tipos MIME para comparar ou analisar a primeira parte do MIME, onde você obtém itens como "texto", "aplicativo" etc.


2
Como file -i...inclui a codificação MIME, você pode usarfile --mime-type -b ...
Wilf

24

Você pode usar o comando "arquivo" se realmente deseja descobrir informações sobre o arquivo, em vez de confiar nas extensões.

Se você se sentir confortável em usar a extensão, poderá usar o grep para verificar se ela corresponde.


sim, eu estou ciente do filecomando. Na verdade, eu tentei fazer a correspondência com base na saída do referido comando ... mas falhei horrivelmente nessas declarações if.

17

Você também pode fazer:

   if [ "${FILE##*.}" = "txt" ]; then
       # operation for txt files here
   fi

11
case $FILE in *.txt ) ... ;; esacpareceria mais robusto e idiomático.
Tripleee

11

Semelhante ao 'arquivo', use o 'mimetype -b', um pouco mais simples, que funcionará independentemente da extensão do arquivo.

if [ $(mimetype -b "$MyFile") == "text/plain" ]
then
  echo "this is a text file"
fi

Edit: você pode precisar instalar o libfile-mimeinfo-perl no seu sistema se o tipo de tipo não estiver disponível


1
Você deve deixar claro que o script 'mimetype' não está disponível em todos os sistemas.
Gilbertpilz

Feito, adicionado libfile-mimeinfo-perl
dargaud 19/05/19

5

A resposta correta sobre como disponibilizar a extensão em um nome de arquivo no linux é:

${filename##*\.} 

Exemplo de impressão de todas as extensões de arquivo em um diretório

for fname in $(find . -maxdepth 1 -type f) # only regular file in the current dir
    do  echo ${fname##*\.} #print extensions 
done

Sua resposta usa uma barra invertida dupla, mas seu exemplo usa apenas uma única barra invertida. Seu exemplo está correto, sua resposta não.
Gilbertpilz 19/05

3

Escrevi um script bash que analisa o tipo de arquivo e depois o copia para um local. Utilizo-o para examinar os vídeos que assisti online do meu cache do Firefox:

#!/bin/bash
# flvcache script

CACHE=~/.mozilla/firefox/xxxxxxxx.default/Cache
OUTPUTDIR=~/Videos/flvs
MINFILESIZE=2M

for f in `find $CACHE -size +$MINFILESIZE`
do
    a=$(file $f | cut -f2 -d ' ')
    o=$(basename $f)
    if [ "$a" = "Macromedia" ]
        then
            cp "$f" "$OUTPUTDIR/$o"
    fi
done

nautilus  "$OUTPUTDIR"&

Ele usa idéias semelhantes às apresentadas aqui, espero que isso seja útil para alguém.


2

Eu acho que '$PATH_TO_SOMEWHERE'é algo assim '<directory>/*'.

Nesse caso, eu mudaria o código para:

find <directory> -maxdepth 1 -type d -exec ... \;
find <directory> -maxdepth 1 -type f -name "*.txt" -exec ... \;

Se você quiser fazer algo mais complicado com o diretório e os nomes dos arquivos de texto, poderá:

find <directory> -maxdepth 1 -type d | while read dir; do echo $dir; ...; done
find <directory> -maxdepth 1 -type f -name "*.txt" | while read txtfile; do echo $txtfile; ...; done

Se você tiver espaços nos nomes dos arquivos, poderá:

find <directory> -maxdepth 1 -type d | xargs ...
find <directory> -maxdepth 1 -type f -name "*.txt" | xargs ...

Estes são ótimos exemplos de como você faz 'loops' no shell. É melhor reservar explícitos fore whileloops para quando o corpo do loop precisar ser mais complexo.
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.