Existe uma maneira conveniente de classificar arquivos como “binários” ou “texto”?

32

Utilitários padrão do Unix, como grep e diff , usam alguma heurística para classificar arquivos como "texto" ou "binário". (Por exemplo, a saída de grep pode incluir linhas como Binary file frobozz matches .)

Existe um teste conveniente que pode ser aplicado em um script zsh para executar uma classificação "texto / binário" semelhante? (Além de algo como grep '' somefile | grep -q Binary .)

(percebo que qualquer teste desse tipo seria necessariamente heurístico e, portanto, imperfeito.)

    
por kjo 10.04.2016 / 18:16

10 respostas

24

Se você perguntar a file apenas para o mime-type , obterá muitos diferentes como text/x-shellscript e application/x-executable etc, mas imagino que, se você verificar o " texto "parte você deve obter bons resultados. Por exemplo ( -b para nenhum nome de arquivo na saída):

file -b --mime-type filename | sed 's|/.*||'
    
por 10.04.2016 / 19:44
20

Outra abordagem seria usar isutf8 da coleção moreutils .

Ele sai com 0 se o arquivo for válido UTF-8 ou ASCII, ou curto-circuitos, imprime uma mensagem de erro (silêncio com -q ) e sai com 1 caso contrário.

    
por 11.04.2016 / 12:21
11

Se você gostar da heurística usada pelo GNU grep , poderá usá-la:

isbinary() {
  LC_MESSAGES=C grep -Hm1 '^' < "${1-$REPLY}" | grep -q '^Binary'
}

Ele procura por bytes NUL no primeiro buffer lido a partir do arquivo (alguns quilo bytes para um arquivo normal, mas pode ser muito menor para um pipe ou soquete ou alguns dispositivos como /dev/random ). Em locales UTF-8, também sinaliza sequências de byte que não formam caracteres UTF-8 válidos. Ele assume que LC_ALL não está definido para algo em que o idioma não é o inglês.

A forma ${1-$REPLY} permite que você a use como um qualificador zsh glob:

ls -ld -- *(.+isbinary)

listaria os arquivos binários .

    
por 11.04.2016 / 13:21
7

Você pode escrever um script que chame file e usar uma instrução de caso para verificar os casos em que está interessado.

Por exemplo

#!/bin/sh
case $(file "$1") in
(*script*|*\ text|*\ text\ *)
    echo text
    ;;
(*)
    echo binary
    ;;
esac

embora, claro, possa haver muitos casos especiais que são de interesse. Apenas verificando strings em uma cópia de libmagic , vejo cerca de 200 casos, por exemplo,

Konqueror cookie text
Korn shell script text executable
LaTeX 2e document text
LaTeX document text
Linux Software Map entry text
Linux Software Map entry text (new format)
Linux kernel symbol map text
Lisp/Scheme program text
Lua script text executable
LyX document text
M3U playlist text
M4 macro processor script text

Alguns usam a string "text" como parte de um tipo diferente, por exemplo,

SoftQuad troff Context intermediate   
SoftQuad troff Context intermediate for AT&T 495 laser printer
SoftQuad troff Context intermediate for HP LaserJet

da mesma forma, script poderia ser parte de uma palavra, mas não vejo problemas neste caso. Mas um script deve verificar "text" como palavra , não substring .

Como lembrete, file output não usa uma descrição precisa que sempre teria "script" ou "texto". Casos especiais são algo a considerar. Um acompanhamento comentou que o --mime-type funciona enquanto essa abordagem não funciona, para arquivos .svg . No entanto, em um teste, vejo esses resultados para arquivos svg:

$ ls -l *.svg
-r--r--r-- 1 tom users  6679 Jul 26  2012 pumpkin_48x48.svg
-r--r--r-- 1 tom users 17372 Jul 30  2012 sink_48x48.svg
-r--r--r-- 1 tom users  5929 Jul 25  2012 vile_48x48.svg
-r--r--r-- 1 tom users  3553 Jul 28  2012 vile-mini.svg
$ file *.svg
pumpkin_48x48.svg: SVG Scalable Vector Graphics image
sink_48x48.svg:    SVG Scalable Vector Graphics image
vile-mini.svg:     SVG Scalable Vector Graphics image
vile_48x48.svg:    SVG Scalable Vector Graphics image
$ file --mime-type *.svg
pumpkin_48x48.svg: image/svg+xml
sink_48x48.svg:    image/svg+xml
vile-mini.svg:     image/svg+xml
vile_48x48.svg:    image/svg+xml

que eu selecionei depois de ver mil arquivos mostram apenas 6 com "texto" na saída do tipo mime. Indiscutivelmente, combinar o "xml" no final da saída do tipo mime poderia ser mais útil, digamos, do que "SVG" correspondente, mas usar um script para fazer que o leve de volta à sugestão feita aqui.

A saída de file requer algum ajuste em ambos os cenários e não é 100% confiável (é confundida por vários dos meus scripts Perl, chamando-os de "dados").

Existe mais de uma implementação de file . O mais comumente usado é o seu trabalho em libmagic , que pode ser usado a partir de programas diferentes (talvez não diretamente de zsh , embora python pode).

De acordo com a Tabela de comparação de testes de arquivos para shell, Perl, Ruby, e Python , o Perl tem uma opção -T que pode ser usada para fornecer essas informações. Mas não lista nenhum recurso comparável para zsh .

Leitura adicional:

por 10.04.2016 / 18:59
6

Você pode tentar determinar se iconv pode ler o arquivo. Isso tem menos desempenho que file (que apenas lê alguns bytes desde o início), mas fornecerá resultados mais confiáveis:

ENCODING=utf-8
if iconv --from-code="$ENCODING" --to-code="$ENCODING" your_file.ext > /dev/null 2>&1; then
    echo text
else
    echo binary
fi

Isso torna iconv basicamente um no-op, mas se encontrar dados inválidos (UTF-8 inválido neste exemplo), irá barfear e sair.

    
por 11.04.2016 / 09:46
3

file tem uma opção --mime-encoding que tenta detectar a codificação de um arquivo.

 $file --mime-encoding Documents/poster2.pdf 
Documents/poster2.pdf: binary
 $file --mime-encoding projects/linux/history-torvalds/Makefile 
projects/linux/history-torvalds/Makefile: us-ascii
 $file --mime-encoding graphe.tex 
Dgraphe.tex: us-ascii
 $file --mime-encoding software.tex 
software.tex: utf-8

Você pode usar file --mime-encoding | grep binary para detectar se um arquivo é um arquivo binário. Ele funciona de forma confiável, embora possa se confundir com um único caractere inválido em um arquivo de texto longo.

Por exemplo, eu alias cat ao seguinte shell script para evitar arruinar meu terminal abrindo inadvertidamente um arquivo binário:

#! /bin/sh -

[ ! -t 1 ] && exec /bin/cat "$@"
for i
do
    if file --mime-encoding -- "$i" | grep -q binary
    then
        hexdump -C -- "$i"
    else
        /bin/cat -- "$i"
    fi
done
    
por 11.04.2016 / 10:17
3

As categorias são arbitrárias. Antes de responder como fazer uma classificação, você precisa de uma definição (estrita). Para ter uma definição, você precisa de um propósito .

Então, o que você quer fazer com essa classificação?

  • Se você quiser selecionar ascii / binary no FTP, é importante não transferir um arquivo binário como ascii (ou ele será corrompido). Então você deve testar se o arquivo é textos simples, html, rtf e alguns outros. Mas na dúvida, selecione binário. E talvez você também queira testar se o arquivo tem apenas um subconjunto como 0x0A, 0x0D e 0x20-0x7F.
  • Se você deseja transferir o arquivo em algum protocolo (POP3, SMTP) você precisa testar para escolher se codificar em base64 ou simplesmente. Nesse caso, você deve testar se há caracteres não suportados.
  • Qualquer outro caso ... pode ter qualquer outra definição.
por 11.04.2016 / 18:10
3
perl -e'chomp(my$f=<>);print "binary$/" if -B $f;print "text$/" if -T _'

fará isso. Consulte a documentação para -B e -T (pesquise nessa página a string The -T and -B switches work as follows ).

    
por 11.04.2016 / 21:31
1

Eu contribuí para o link Ele não possui um wrapper de linha de comando (ainda), mas essa é uma biblioteca Python simples, fácil de chamar até mesmo da CLI. Ele usa uma heurística razoavelmente eficiente para determinar se um arquivo é texto ou binário.

    
por 22.08.2016 / 00:12
1

Eu agora esta resposta é um pouco antiga, mas acho que meu amigo me ensinou um ótimo "hack" para fazer isso.

Você usa o comando diff e verifica seu arquivo em um arquivo de texto de teste:

$ diff filetocheck testfile.txt

Agora, se filetocheck for um arquivo binário, a saída seria:

Binary files filetocheck and testfile.txt differ

Dessa forma, você pode aproveitar o comando diff e, por exemplo, escreva uma função que faz a verificação em um script.

Espero que isso ajude alguém.

    
por 06.11.2017 / 17:43

Tags