Localizando todos os arquivos "Não Binários"

38

É possível usar o comando find para encontrar todos os arquivos "não binários" em um diretório? Aqui está o problema que estou tentando resolver.

Recebi um arquivo de arquivos de um usuário do Windows. Este arquivo contém código fonte e arquivos de imagem. Nosso sistema de compilação não funciona bem com arquivos que possuem finais de linha do Windows. Eu tenho um programa de linha de comando ( flip -u ) que irá inverter os finais de linha entre * nix e windows. Então, eu gostaria de fazer algo assim

find . -type f | xargs flip -u

No entanto, se esse comando for executado em um arquivo de imagem ou outro arquivo de mídia binário, ele corromperá o arquivo. Percebo que posso criar uma lista de extensões de arquivo e filtrar com isso, mas prefiro ter algo que não dependa de mim para manter essa lista atualizada.

Portanto, existe uma maneira de encontrar todos os arquivos não binários em uma árvore de diretórios? Ou existe uma solução alternativa que eu deveria considerar?

    
por Alan Storm 24.08.2012 / 20:46

9 respostas

19

Eu usaria file e canalizaria a saída para grep ou awk para encontrar arquivos de texto, e então extrairia apenas a parte do nome do arquivo da saída de file e canalizaria isso para xargs.

algo como:

file * | awk -F: '/ASCII text/ {print $1}' | xargs -d'\n' -r flip -u

Note que o grep procura por 'texto ASCII' em vez de apenas 'texto' - você provavelmente não quer mexer com documentos em Rich Text ou arquivos de texto unicode, etc.

Você também pode usar find (ou qualquer outra coisa) para gerar uma lista de arquivos para examinar com file :

find /path/to/files -type f -exec file {} + | \
  awk -F: '/ASCII text/ {print $1}' | xargs -d'\n' -r flip -u

O argumento -d'\n' para xargs faz com que os xargs tratem cada linha de entrada como um argumento separado, atendendo assim aos nomes de arquivos com espaços e outros caracteres problemáticos. ou seja, é uma alternativa para xargs -0 quando a origem de entrada não gera ou não pode gerar saída separada por NULL (como find 's -print0 option). De acordo com o changelog, xargs tem a opção -d / --delimiter em setembro de 2005, então deve estar em qualquer distro linux não antiga (eu não tinha certeza, e é por isso que eu verifiquei - eu apenas me lembrava vagamente que era um " recente "adição".

Observe que um avanço de linha é um caractere válido em nomes de arquivos, portanto, isso será interrompido se algum nome de arquivo tiver alimentação de linha neles. Para usuários unix típicos, isso é patologicamente insano, mas não é inédito se os arquivos foram originados em máquinas Mac ou Windows.

Observe também que file não é perfeito. É muito bom em detectar o tipo de dados em um arquivo, mas pode ficar confuso ocasionalmente.

Eu tenho usado inúmeras variações deste método muitas vezes no passado com sucesso.

    
por 25.08.2012 / 03:15
9

Não. Não há nada de especial sobre um arquivo binário ou não-binário. Você pode usar heurísticas como 'contém apenas caracteres em 0x01–0x7F', mas isso chamará arquivos de texto com arquivos binários de caracteres não-ASCII e arquivos de texto de arquivos binários sem sorte.

Agora, uma vez que você ignorou isso ...

arquivos zip

Se for proveniente do seu usuário do Windows como um arquivo zip, o formato zip suporta a marcação de arquivos como binários ou texto no próprio arquivo. Você pode usar a opção -a do unzip para prestar atenção nisso e converter. É claro, veja o primeiro parágrafo para saber por que isso pode não ser uma boa ideia (o programa zip pode ter errado quando o arquivo foi feito).

O zipinfo informará quais arquivos são binários (b) ou text (t) em sua listagem zipfile.

outros arquivos

O comando file irá olhar para um arquivo e tentar identificá-lo. Em particular, você provavelmente encontrará a opção -i (tipo MIME de saída) útil; apenas converter arquivos com texto tipo / *

    
por 24.08.2012 / 21:00
6

Uma solução geral para processar somente arquivos não binários em bash usando file -b --mime-encoding :

while IFS= read -d '' -r file; do
  [[ "$(file -b --mime-encoding "$file")" = binary ]] &&
    { echo "Skipping   $file."; continue; }

  echo "Processing $file."

  # ...

done < <(find . -type f -print0)

Entrei em contato com o autor do utilitário file e ele adicionou um bonito -00 paramter na versão 5.26 (lançado 2016-04-16, está, por exemplo, no Arch atual e no Ubuntu 16.10) que imprime fileawkresultORS para vários arquivos alimentados de uma só vez, desta forma você pode fazer, por exemplo:

find . -type f -exec file -00 --mime-encoding {} + |
  awk 'BEGIN{ORS=RS="
while IFS= read -d '' -r file; do

  echo "Processing $file."

  # ...

done < <(find . -type f -exec file -00 --mime-encoding {} + |
  awk 'BEGIN{ORS=RS="
#!/bin/bash

# Expects files as arguments and returns the ones that do
# not appear to be binary files as a zero-separated list.
#
# USAGE:
#   filter_binary_files.sh [FILES...]
#
# EXAMPLE:
#   find . -type f -mtime +5 -exec ./filter_binary_files.sh {} + | xargs -0 ...
# 

[[ $# -eq 0 ]] && exit

if [[ "$(file -v)" =~ file-([1-9][0-9]|[6-9]|5\.([3-9][0-9]|2[6-9])) ]]; then
  file -00 --mime-encoding -- "$@" |
    awk 'BEGIN{ORS=RS="
#!/bin/sh

# Expects files as arguments and returns the ones that do
# not appear to be binary files as a zero-separated list.
#
# USAGE:
#   filter_binary_files.sh [FILES...]
#
# EXAMPLE:
#   find . -type f -mtime +5 -exec ./filter_binary_files.sh {} + | xargs -0 ...
# 

[ $# -eq 0 ] && exit

if [ "$(printf '%s\n' 'file-5.26' "$(file -v | head -1)" | sort -V)" = \
    'file-5.26' ]; then
  file -00 --mime-encoding -- "$@" |
    awk 'BEGIN{ORS=RS="
while IFS= read -d '' -r file; do
  [[ "$(file -b --mime-encoding "$file")" = binary ]] &&
    { echo "Skipping   $file."; continue; }

  echo "Processing $file."

  # ...

done < <(find . -type f -print0)
"}{if(NR%2)f=$0;else if(!/binary/)print f}' else for f do [ "$(file -b --mime-encoding -- "$f")" != binary ] && printf '%s
find . -type f -exec file -00 --mime-encoding {} + |
  awk 'BEGIN{ORS=RS="
while IFS= read -d '' -r file; do

  echo "Processing $file."

  # ...

done < <(find . -type f -exec file -00 --mime-encoding {} + |
  awk 'BEGIN{ORS=RS="
#!/bin/bash

# Expects files as arguments and returns the ones that do
# not appear to be binary files as a zero-separated list.
#
# USAGE:
#   filter_binary_files.sh [FILES...]
#
# EXAMPLE:
#   find . -type f -mtime +5 -exec ./filter_binary_files.sh {} + | xargs -0 ...
# 

[[ $# -eq 0 ]] && exit

if [[ "$(file -v)" =~ file-([1-9][0-9]|[6-9]|5\.([3-9][0-9]|2[6-9])) ]]; then
  file -00 --mime-encoding -- "$@" |
    awk 'BEGIN{ORS=RS="
#!/bin/sh

# Expects files as arguments and returns the ones that do
# not appear to be binary files as a zero-separated list.
#
# USAGE:
#   filter_binary_files.sh [FILES...]
#
# EXAMPLE:
#   find . -type f -mtime +5 -exec ./filter_binary_files.sh {} + | xargs -0 ...
# 

[ $# -eq 0 ] && exit

if [ "$(printf '%s\n' 'file-5.26' "$(file -v | head -1)" | sort -V)" = \
    'file-5.26' ]; then
  file -00 --mime-encoding -- "$@" |
    awk 'BEGIN{ORS=RS="%pre%"}{if(NR%2)f=$0;else if(!/binary/)print f}'
else
  for f do
    [ "$(file -b --mime-encoding -- "$f")" != binary ] &&
      printf '%s%pre%' "$f"
  done
fi
"}{if(NR%2)f=$0;else if(!/binary/)print f}' else for f do [[ "$(file -b --mime-encoding -- "$f")" != binary ]] && printf '%s%pre%' "$f" done fi
"}{if(NR%2)f=$0;else if(!/binary/)print f}')
"}{if(NR%2)f=$0;else if(!/binary/)print f}' | …
' "$f" done fi
"}{if(NR%2)f=$0;else if(!/binary/)print f}' else for f do [[ "$(file -b --mime-encoding -- "$f")" != binary ]] && printf '%s%pre%' "$f" done fi
"}{if(NR%2)f=$0;else if(!/binary/)print f}')
"}{if(NR%2)f=$0;else if(!/binary/)print f}' | …

(A parte bash é filtrar todos os arquivos que não sejam não binários. -00 é o separador de saída.)

Também pode ser usado em um loop:

%pre%

Com base nisso e no anterior, criei um pequeno script file para filtrar arquivos binários que utilizam o novo método usando o parâmetro sort -V de %code% em versões mais recentes e usando o método anterior em versões mais antigas:

%pre%

Ou aqui mais um POSIX-y, mas requer suporte para %code% :

%pre%     
por 02.03.2016 / 12:10
4

A resposta de Cas é boa, mas assume nomes de arquivos sane ; em particular, assume-se que os nomes dos arquivos não conterão novas linhas.

Não há uma boa razão para fazer essa suposição aqui, já que é bem simples (e, na verdade, mais limpo na minha opinião) lidar corretamente com esse caso:

find . -type f -exec sh -c 'file "$1" | grep -q "ASCII text"' sh {} \; -exec flip -u {} \;

O comando find só faz uso de recursos especificados pelo POSIX . Usar -exec para executar comandos arbitrários como testes booleanos é simples, robusto (lida com nomes de arquivos estranhos corretamente) e mais portátil que -print0 .

Na verdade, todas as partes do comando são especificadas pelo POSIX, exceto flip .

Observe que file não garante a precisão dos resultados retornados. No entanto, na prática, o grepping para "texto ASCII" em sua saída é bastante confiável.

(Talvez perca alguns arquivos de texto, mas é muito improvável identificar incorretamente um arquivo binário como "texto ASCII" e mangle-lo - então estamos errando do lado da cautela.)

    
por 05.11.2016 / 17:01
4

A resposta aceita não encontrou todos eles para mim. Aqui está um exemplo usando -I do grep para ignorar binários e ignorando todos os arquivos ocultos ...

find . -type f -not -path '*/\.*' -exec grep -Il '.' {} \; | xargs -L 1 echo 

Aqui está em uso em uma aplicação prática: dos2unix

link

    
por 17.05.2017 / 19:37
2
find . -type f -exec grep -I -q . {} \; -print

Isso encontrará todos os arquivos regulares ( -type f ) no diretório atual (ou abaixo) que grep considera não-vazios e não-binários.

Ele usa grep -I para distinguir entre arquivos binários e não binários. O -I flag e fará com que grep saia com um status de saída diferente de zero quando detectar que um arquivo é binário. Um arquivo "binário" é, de acordo com grep , um arquivo que contém caracteres fora do intervalo ASCII imprimível.

A opção -q para grep fará com que ela saia com um status de saída zero se o padrão fornecido for encontrado, sem emitir nenhum dado. O padrão que usamos é um único ponto, que corresponderá a qualquer caractere.

Se o arquivo não for binário e contiver pelo menos um caractere, o nome do arquivo será impresso.

Se você se sentir corajoso, também pode conectar seu flip -u a ele:

find . -type f -exec grep -I -q . {} \; -print -exec flip -u {} \;
    
por 17.05.2017 / 22:09
1

Tente isto:

find . -type f -print0 | xargs -0 -r grep -Z -L -U '[^         -~]' | xargs -0 -r flip -u

Onde o argumento de grep '[^ -~]' é '[^<tab><space>-~]' .

Se você digitá-lo em uma linha de comando shell, digite Ctrl + V antes da aba . Em um editor, não deve haver problema.

  • '[^<tab><space>-~]' corresponderá a qualquer caractere que não seja texto ASCII (retornos de carro são ignorados por grep ).
  • -L imprimirá apenas o nome do arquivo dos arquivos que não corresponderem
  • -Z produzirá nomes de arquivos separados por um caractere nulo (por xargs -0 )
por 06.01.2017 / 16:24
1

Solução alternativa:

O comando dos2unix irá converter os terminais de linha do Windows CRLF para o Unix LF e ignorar automaticamente os arquivos binários. Eu aplico-o recursivamente usando:

find . -type f -exec dos2unix {} \;
    
por 21.09.2017 / 22:08
0

sudo find / (-tipo f -e -path '* / git / *' -inome 'README') -exec grep -liI '100644 \ | 100755' {} \; -exec flip -u {} \;

i. (-tipo f -e -path '* / git / *' -inome 'README'): procura arquivos dentro de um caminho contendo o nome git e o arquivo com o nome README. Se você conhece alguma pasta e nome de arquivo específicos para procurar, será útil.

O comando ii.-exec executa um comando no nome do arquivo gerado por find

iii. \; indica fim de comando

iv. {} é a saída do arquivo / nome da pasta encontrado na pesquisa de localização anterior

v.Muitos comandos podem ser executados posteriormente. Adicionando -exec "command" \; como com -exec flip -u \;

vii.grep

1.-l lists the name of the file
2.-I searches only non-binary files
3.-q quiet output
4.'100644\|100755' searches for either 100644 or 100755 within the file found. if found it then runs flip -u. \| is the or operator for grep. 

você pode clonar este diretório de teste e testá-lo: link

resposta mais detalhada aqui: link

    
por 04.09.2017 / 23:04