Trovare tutti i file "non binari"


43

È possibile utilizzare il findcomando per trovare tutti i file "non binari" in una directory? Ecco il problema che sto cercando di risolvere.

Ho ricevuto un archivio di file da un utente di Windows. Questo archivio contiene codice sorgente e file immagine. Il nostro sistema di compilazione non funziona bene con i file che hanno terminazioni di linea di Windows. Ho un programma a riga di comando ( flip -u) che capovolge le terminazioni di riga tra * nix e windows. Quindi, mi piacerebbe fare qualcosa del genere

find . -type f | xargs flip -u

Tuttavia, se questo comando viene eseguito su un file di immagine o un altro file multimediale binario, danneggerà il file. Mi rendo conto che potrei costruire un elenco di estensioni di file e filtrare con quello, ma preferirei avere qualcosa che non dipende da me mantenendo tale elenco aggiornato.

Quindi, c'è un modo per trovare tutti i file non binari in un albero di directory? O c'è una soluzione alternativa che dovrei considerare?


1
È possibile utilizzare l' fileutilità da qualche parte nel proprio script / pipeline per identificare se il file è costituito da dati o testo
lk-

1
Cosa intendi per non binario (tutto su un computer moderno è binario). Immagino che tu stia usando la distinzione dal vecchio sistema operativo C / PM, che aveva file di testo e binari. I file di testo potrebbero essere di qualsiasi lunghezza, ma dovevano terminare con un ctrl-z e i file binari dovevano essere un multiplo di un blocco di 512 byte. Se è così intendi file di testo. (Noto anche che scrivi della fine della linea in file non binari, questo suggerirebbe anche che sono file di testo) È corretto?
ctrl-alt-delor

Tutti i file sono binari, è solo una questione di interpretazione. Stai chiedendo come trovare file di testo?
ctrl-alt-delor,

@richard Vengo da un'era in cui chiamavamo file che dovevano essere interpretati come testo in chiaro e tutti gli altri file (immagini, documenti di elaborazione testi, ecc.) binari. So che è tutto solo uno e zeri sotto il cofano :)
Alan Storm,

1
Ah, capisco cosa intendi per i miei termini: userò binario / testo in futuro per evitare confusione. Ri: la cosa \ r \ n - capisco che sono i caratteri ASCII per il ritorno a capo di una macchina da scrivere (sposta all'inizio della riga) e l'avanzamento di riga (sposta in basso di una riga). Quindi \ r \ n è un modello "più preciso" della cosa fisica del mondo reale a cui un personaggio di fine linea era destinato. Prima di OS X, i Mac usavano solo una r per questo. Di solito scrivo tutto come "scelte arbitrarie fatte in una corsa con cui abbiamo ancora a che fare"
Alan Storm

Risposte:


20

Userei filee reindirizzerei l'output in grep o awk per trovare i file di testo, quindi estrarrei solo la parte del nome del file filedell'output e lo instraderei in xargs.

qualcosa di simile a:

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

Si noti che grep cerca "testo ASCII" anziché qualsiasi "testo" - probabilmente non si desidera fare confusione con documenti Rich Text o file di testo Unicode ecc.

Puoi anche usare find(o qualsiasi altra cosa) per generare un elenco di file da esaminare con file:

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

L' -d'\n'argomento di xargs fa sì che xargs tratti ciascuna riga di input come un argomento separato, fornendo così nomi di file con spazi e altri caratteri problematici. ovvero è un'alternativa a xargs -0quando la sorgente di input non genera o non può generare output separato da NULL (come findl' -print0opzione). Secondo il log delle modifiche, xargs ha ottenuto l' opzione -d/ --delimiternel settembre 2005, quindi dovrebbe essere in qualsiasi distro linux non antico (non ero sicuro, motivo per cui ho controllato - ho solo vagamente ricordato che era un'aggiunta "recente").

Si noti che un avanzamento riga è un carattere valido nei nomi di file, quindi questo si interromperà se eventuali nomi di file contengono avanzamenti di riga. Per gli utenti unix tipici, questo è patologicamente folle, ma non è inaudito se i file sono originati su computer Mac o Windows.

Si noti inoltre che filenon è perfetto. È molto bravo a rilevare il tipo di dati in un file ma a volte può essere confuso.

Ho usato numerose varianti di questo metodo molte volte in passato con successo.


1
Grazie per questa soluzione! Per qualche motivo fileviene visualizzato English textanziché ASCII textsul mio sistema Solaris, quindi ho modificato di conseguenza quella parte. Inoltre, ho sostituito awk -F: '{print $1}'con l'equivalente cut -f1 -d:.
Andrew Cheong,

3
vale la pena dire grep -Ifiltri binari
xenoterracide

Cercare la parola textdovrebbe essere sufficiente. Questo raccoglierà anche filedescrizioni come ASCII Java program texto HTML document texto troff or preprocessor input text.
user1024

La mia risposta è parzialmente una risposta / miglioramento su questa risposta. Ottimo punto su grepping per ASCII textevitare di rovinare RTF.
Wildcard il

1
xenoterracide: Mi hai salvato la vita amico! Solo una bandiera -I e BINGO
Sergio Abreu il

9

No. Non c'è nulla di speciale in un file binario o non binario. Puoi usare euristiche come 'contiene solo caratteri in 0x01–0x7F', ma questo chiamerà file di testo con file binari di caratteri non ASCII e sfortunati file di testo di file binari.

Ora, una volta ignorato che ...

file zip

Se proviene dall'utente Windows come file zip, il formato zip supporta la marcatura dei file come binari o di testo nell'archivio stesso. È possibile utilizzare l' -aopzione di decompressione per prestare attenzione a questo e convertire. Ovviamente, vedi il primo paragrafo per capire perché questa potrebbe non essere una buona idea (il programma zip potrebbe aver indovinato quando ha creato l'archivio).

zipinfo ti dirà quali file sono binari (b) o text (t) nella sua lista di file zip.

altri file

Il comando file guarderà un file e proverà a identificarlo. In particolare, probabilmente troverai -iutile l'opzione (tipo MIME di output); converti solo file con tipo text / *


6

Una soluzione generale per elaborare solo file non binari bashutilizzando 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)

Ho contattato l'autore dell'utilità del file e ha aggiunto un -00parametro elegante nella versione 5.26 (rilasciato il 16-04-2016, è ad esempio nell'attuale Arch e Ubuntu 16.10) che stampa file\0result\0per più file alimentati contemporaneamente, in questo modo puoi farlo per esempio:

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

(La awkparte è filtrare ogni file che non sia non binario. ORSÈ il separatore di output.)

Naturalmente può anche essere usato in un ciclo:

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}')

Sulla base di questo e del precedente ho creato un piccolo bashscript per filtrare i file binari che utilizza il nuovo metodo usando il -00parametro filenelle versioni più recenti di esso e ricade nel metodo precedente nelle versioni precedenti:

#!/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

O qui più POSIX-y, ma richiede il supporto per 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

La risposta accettata non le ha trovate tutte per me. Ecco un esempio che utilizza grep -Iper ignorare i binari e ignorare tutti i file nascosti ...

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

Qui è in uso in un'applicazione pratica: dos2unix

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


4

La risposta di Cas è buona, ma assume nomi di file sani ; in particolare si presume che i nomi dei file non conterranno newline.

Non ci sono buoni motivi per fare questo assunto qui, dal momento che è abbastanza semplice (e in realtà più pulito secondo me) anche gestire correttamente quel caso:

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

Il findcomando utilizza solo le funzionalità specificate da POSIX . L'uso -execper eseguire comandi arbitrari come test booleani è semplice, robusto (gestisce correttamente nomi di file dispari) e più portatile di -print0.

In effetti, tutte le parti del comando sono specificate da POSIX ad eccezione di flip.

Nota che filenon garantisce l'accuratezza dei risultati che restituisce. Tuttavia, in pratica il grepping per "testo ASCII" nel suo output è abbastanza affidabile.

(Forse potrebbero mancare alcuni file di testo, ma è molto improbabile che identifichi erroneamente un file binario come "testo ASCII" e lo manipoli, quindi stiamo sbagliando dalla parte della cautela.)


Il file senza argomenti callspuò essere piuttosto lento, ad esempio per i video ti dirà tutto sulla codifica.
phk,

Inoltre stai assumendo che nessun file inizi con -.
phk,

E non vedo alcun motivo per cui non si debba fare una sola chiamata file, può richiedere più file come argomenti.
phk,

@phk, per rispondere ai tuoi commenti: (1) è bene conoscere la potenziale lentezza, ma non vedo alcun modo POSIX per impedirlo; (2) Faccio zero ipotesi sui nomi dei file, poiché il findcomando prefigura ./qualsiasi nome di file passato al comando shell; (3) L'utilizzo grepcome test su un singolo fileoutput di comando alla volta è l'unico modo POSIX che posso vedere per garantire la corretta gestione dei nomi dei file che possono contenere righe.
Wildcard il

Ho esaminato la tua soluzione "POSIX-y" finale e penso che sia intelligente, ma supponi che filesupporti la --mime-encodingbandiera e il --separatore, nessuno dei quali è garantito da POSIX .
Carattere jolly

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

Questo troverà tutti i file regolari ( -type f) nella directory corrente (o sotto) che grepritiene non vuoti e non binari.

Usa grep -Iper distinguere tra file binari e non binari. Il -Iflag e causerà grepl'uscita con uno stato di uscita diverso da zero quando rileva che un file è binario. Un file "binario" è, secondo grep, un file che contiene caratteri al di fuori dell'intervallo ASCII stampabile.

L' -qopzione per grepfar sì che si chiuda con uno stato di uscita pari a zero se viene trovato il modello dato, senza emettere alcun dato. Il modello che usiamo è un singolo punto, che corrisponderà a qualsiasi carattere.

Se il file risulta non binario e contiene almeno un carattere, viene stampato il nome del file.

Se ti senti coraggioso, puoi anche collegarti flip -u:

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

1

Prova questo :

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

Dove l'argomento di grep '[^ -~]'IS '[^<tab><space>-~]'.

Se lo digiti su una riga di comando della shell, digita Ctrl+ Vprima Tab. In un editor, non dovrebbero esserci problemi.

  • '[^<tab><space>-~]'corrisponderà a qualsiasi carattere che non sia testo ASCII (i ritorni a capo vengono ignorati da grep).
  • -L stamperà solo il nome file dei file che non corrispondono
  • -Zprodurrà nomi di file separati da un carattere null (per xargs -0)

Vale la pena notare che con Regex simile a Perl grep -P(se disponibile) \tè disponibile. In alternativa, usare la traduzione locale se la shell lo supporta: $'\t'( bashe zshdo).
phk,

1

Soluzione alternativa:

Il comando dos2unix converte le terminazioni di linea da Windows CRLF a Unix LF e salta automaticamente i file binari. Lo applico in modo ricorsivo utilizzando:

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

Dal momento che dos2unixpuò prendere più nomi di file come argomento, è molto più efficiente farlofind . -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'): cerca i file all'interno di un percorso contenente il nome git e il file con il nome README. Se conosci una cartella e un nome file specifici da cercare, sarà utile.

Il comando ii.-exec esegue un comando sul nome del file generato da find

iii \.; indica la fine del comando

iv. {} è l'output del file / nome utente trovato dalla precedente ricerca di ricerca

v.I comandi multipli possono essere eseguiti successivamente. Aggiungendo -exec "command" \; come con -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. 

puoi clonare questa directory di prova e provarla: https://github.com/alphaCTzo7G/stackexchange/tree/master/linux/findSolution204092017

risposta più dettagliata qui: https://github.com/alphaCTzo7G/stackexchange/blob/master/linux/findSolution204092017/README.md

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.