Localizando todos os arquivos "não binários"


43

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

Eu 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 com finais de linha do Windows. Eu tenho um programa de linha de comando ( flip -u) que irá inverter as terminações 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 em outro arquivo de mídia binário, ele corromperá o arquivo. Sei que poderia criar uma lista de extensões de arquivo e filtrar com isso, mas prefiro ter algo que não depende de mim para manter essa lista atualizada.

Então, 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 devo considerar?


1
Você pode usar a filealgum lugar utilidade em seu script / gasoduto para identificar se o arquivo é de dados ou texto
lk-

1
O que você quer dizer com não-binário (tudo em um computador moderno é binário). Suponho que você esteja usando a distinção do antigo sistema operacional C / PM, que tinha arquivos de texto e binários. Os arquivos de texto podiam ter qualquer tamanho, mas tinham que terminar com um ctrl-z, e os arquivos binários tinham que ser múltiplos de um bloco de 512 bytes. Nesse caso, você está significando arquivo de texto. (Observe também que você escreve sobre o final de linha em arquivos não binários, isso também sugere que sejam arquivos de texto) Isso está correto?
CTRL-ALT-DELOR

Todos os arquivos são binários, é apenas uma questão de interpretação. Você está perguntando como encontrar arquivos de texto?
Ctrl-alt-delor 17/05/19

@richard Venho de uma época em que chamamos arquivos que deveriam ser interpretados como texto sem formatação e todos os outros arquivos (imagens, documentos de processamento de texto etc.) binários. Eu sei que é tudo
1777 Alan Storm

1
Ah, entendo o que você quer dizer com meus termos - usarei binário / texto no futuro para evitar confusão. Re: the \ r \ n coisa - é meu entender esses são os caracteres ASCII para o retorno de carro de uma máquina de escrever (vá para o início da linha) e o avanço de linha (desça uma linha). Portanto, \ r \ n é um modelo "mais preciso" da coisa física do mundo real para a qual um caractere de fim de linha era. Antes do OS X, os Macs usavam apenas um \ r para isso. Eu costumo escrever a coisa toda como "escolhas arbitrárias feitas com pressa com as quais ainda estamos lidando"
Alan Storm

Respostas:


20

Eu usaria filee canalizaria a saída no grep ou awk para encontrar arquivos de texto, depois extrairia apenas a parte do nome do arquivo da filesaída e a canalizaria para o xargs.

algo como:

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

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

Você também pode usar find(ou o que seja) para gerar uma lista de arquivos para examinar file:

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

O -d'\n'argumento para xargs faz com que os xargs tratem cada linha de entrada como um argumento separado, atendendo a nomes de arquivos com espaços e outros caracteres problemáticos. isto é, uma alternativa para xargs -0quando a fonte de entrada não gera ou não pode gerar saída separada por NULL (como finda -print0opção de). De acordo com o changelog, o xargs obteve a opção -d/ --delimiterem setembro de 2005, portanto deve estar em qualquer distribuição Linux não antiga (não tinha certeza, e foi por isso que verifiquei - apenas me lembrei vagamente que era uma adição "recente").

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

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

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


1
Obrigado por esta solução! Por algum motivo, é fileexibido em English textvez de ASCII textno meu sistema Solaris, então modifiquei essa parte de acordo. Além disso, substituí awk -F: '{print $1}'pelo equivalente cut -f1 -d:.
Andrew Cheong

3
Vale dizer grep -Ifiltros binários
xenoterracide

Procurar a palavra textdeve ser suficiente. Isso também selecionará filedescrições como ASCII Java program textou HTML document textou troff or preprocessor input text.
precisa saber é o seguinte

Minha resposta é parcialmente uma resposta / melhoria dessa resposta. Muito bom ponto de grepping para ASCII textevitar bagunçar RTFs.
Curinga

1
xenoterracide: Você salvou minha vida, cara! Apenas uma bandeira -I e BINGO
Sergio Abreu

9

Não. Não há nada de especial em 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 azarados.

Agora, depois de ignorar isso ...

arquivos zip

Se for proveniente do usuário do Windows como um arquivo zip, o formato zip suporta a marcação de arquivos como binários ou como texto no próprio arquivo morto. Você pode usar a -aopção de descompactar para prestar atenção e converter. Obviamente, veja no primeiro parágrafo o motivo pelo qual isso pode não ser uma boa ideia (o programa zip pode ter adivinhado errado ao criar o arquivo).

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

outros arquivos

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


6

Uma solução geral para processar apenas arquivos não binários bashusando 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 de arquivo e ele adicionou um -00parâmetro bacana na versão 5.26 (lançada em 16/04/2016, por exemplo, no atual Arch e Ubuntu 16.10), que imprime file\0result\0para vários arquivos alimentados a ele de uma só vez, desta maneira você pode fazer por exemplo:

find . -type f -exec file -00 --mime-encoding {} + |
  awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}' | 

(A awkparte é filtrar todos os arquivos que não são binários. ORSÉ o separador de saída.)

Também pode ser usado em um loop, é claro:

while IFS= read -d '' -r file; do

  echo "Processing $file."

  # ...

done < <(find . -type f -exec file -00 --mime-encoding {} + |
  awk 'BEGIN{ORS=RS="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}')

Com base nisso e no anterior, criei um pequeno bashscript para filtrar arquivos binários que utilizam o novo método usando o -00parâmetro de filenas versões mais recentes e voltando ao método anterior nas versões mais antigas:

#!/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="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}'
else
  for f do
    [[ "$(file -b --mime-encoding -- "$f")" != binary ]] &&
      printf '%s\0' "$f"
  done
fi

Ou aqui mais um POSIX-y, mas requer suporte para sort -V:

#!/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="\0"}{if(NR%2)f=$0;else if(!/binary/)print f}'
else
  for f do
    [ "$(file -b --mime-encoding -- "$f")" != binary ] &&
      printf '%s\0' "$f"
  done
fi

6

A resposta aceita não encontrou todas elas para mim. Aqui está um exemplo usando grep's -Ipara 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

https://unix.stackexchange.com/a/365679/112190


4

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

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

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

O findcomando utiliza apenas recursos especificados pelo POSIX . Usar -execpara executar comandos arbitrários como testes booleanos é simples, robusto (manipula nomes de arquivos ímpares corretamente) e mais portáteis que -print0.

De fato, todas as partes do comando são especificadas pelo POSIX, exceto por flip.

Observe que filenã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 ele perca alguns arquivos de texto, mas é muito improvável que identifique incorretamente um arquivo binário como "texto ASCII" e o altere - portanto, estamos errando por precaução.)


O arquivo sem argumentos callspode ser bastante lento, por exemplo, para vídeos, ele mostra tudo sobre a codificação.
Phk #

Você também está assumindo que nenhum arquivo começa com -.
Phk #

E não vejo razão para que você não faça apenas uma chamada file, pode levar vários arquivos como argumentos.
Phk #

@phk, para endereçar seus comentários: (1) é bom conhecer a lentidão potencial, mas não vejo uma maneira POSIX de impedir isso; (2) Eu faço de zero suposições sobre nomes de arquivos, como o findcomando irá anteceder ./a qualquer nome de arquivo passado para o comando shell; (3) Usar grepcomo teste em uma única filesaída de comando por vez é a única maneira POSIX que eu posso ver para garantir o manuseio correto dos nomes de arquivos que podem conter novas linhas.
Curinga

Examinei sua solução final "POSIX-y" e acho que é inteligente - mas você supõe que filesuporta a --mime-encodingflag e o --separador, nenhum dos quais é garantido pelo POSIX .
Curinga

2
find . -type f -exec grep -I -q . {} \; -print

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

Ele usa grep -Ipara distinguir entre arquivos binários e não binários. O -Isinalizador e fará com grepque 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 -qopção para grepfará com que ele 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 for não-binário e se contiver pelo menos um caractere, o nome do arquivo será impresso.

Se você se sente corajoso, também pode conectar-se a flip -uele:

find . -type f -exec grep -I -q . {} \; -print -exec flip -u {} \;

1

Tente o seguinte:

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ê digitar em uma linha de comando do shell, digite Ctrl+ Vantes Tab. Em um editor, não deve haver problema.

  • '[^<tab><space>-~]'corresponderá a qualquer caractere que não seja texto ASCII (os retornos de carro são ignorados por grep).
  • -L imprimirá apenas o nome do arquivo dos arquivos que não correspondem
  • -Zproduzirá nomes de arquivos separados por um caractere nulo (para xargs -0)

Vale a pena notar que o Regex semelhante ao Perl grep -P(se disponível) \testá disponível. Como alternativa, usando a tradução de código de idioma se o shell oferecer suporte: $'\t'( bashe zshfaça).
Phk #

1

Solução alternativa:

O comando dos2unix converterá as terminações de linha do Windows CRLF em Unix LF e ignorará automaticamente os arquivos binários. Aplico-o recursivamente usando:

find . -type f -exec dos2unix {} \;

Desde dos2unixpode levar vários nomes de arquivos como argumento, é muito mais eficiente para fazerfind . -type f -exec dos2unix {} +
Anthon

0

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

i. (-type f -and -path '* / git / *' -iname 'README'): pesquisa arquivos em um caminho que contém o nome git e o arquivo com o nome README. Se você conhece alguma pasta e nome de arquivo específicos para pesquisar, será útil.

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

iii. \; indica final do comando

iv. {} é a saída do arquivo / nome da pasta encontrado na pesquisa anterior

v.Múltiplos comandos podem ser executados posteriormente. Anexando -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: https://github.com/alphaCTzo7G/stackexchange/tree/master/linux/findSolution204092017

resposta mais detalhada aqui: https://github.com/alphaCTzo7G/stackexchange/blob/master/linux/findSolution204092017/README.md

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.