Alle "Nicht-Binär" -Dateien finden

43

Ist es möglich, mit dem findBefehl alle "nicht-binären" Dateien in einem Verzeichnis zu finden? Hier ist das Problem, das ich zu lösen versuche.

Ich habe ein Archiv mit Dateien von einem Windows-Benutzer erhalten. Dieses Archiv enthält Quellcode und Bilddateien. Unser Build-System spielt nicht gut mit Dateien, die Windows-Zeilenenden haben. Ich habe ein Kommandozeilenprogramm ( flip -u), das die Zeilenenden zwischen * nix und windows wechselt. Also, ich würde gerne so etwas machen

find . -type f | xargs flip -u

Wenn dieser Befehl jedoch für eine Image-Datei oder eine andere binäre Mediendatei ausgeführt wird, wird die Datei beschädigt. Mir ist klar, dass ich damit eine Liste mit Dateierweiterungen und Filtern erstellen kann, aber ich möchte lieber etwas, das nicht darauf angewiesen ist, dass ich diese Liste auf dem neuesten Stand halte.

Gibt es eine Möglichkeit, alle nicht-binären Dateien in einem Verzeichnisbaum zu finden? Oder gibt es eine alternative Lösung, die ich in Betracht ziehen sollte?

Alan Storm
quelle
1
Sie könnten die Verwendung fileDienstprogramm irgendwo in Ihrem Skript / Pipeline zu identifizieren , ob die Datei Daten oder Text
LK-
1
Was meinst du mit nicht-binär (alles auf einem modernen Computer ist binär). Ich vermute, Sie verwenden die Unterscheidung von dem alten C / PM-Betriebssystem, das Text- und Binärdateien enthielt. Textdateien konnten beliebig lang sein, mussten jedoch mit Strg-Z enden, und Binärdateien mussten ein Vielfaches eines 512-Byte-Blocks sein. Wenn ja, meinen Sie Textdatei. (Ich stelle auch fest, dass Sie über Zeilenenden in nicht-binären Dateien schreiben. Dies würde auch nahelegen, dass es sich um Textdateien handelt.) Stimmt das?
Strg-Alt-Delor
Alle Dateien sind binär, es ist nur eine Frage der Interpretation. Fragen Sie, wie Sie Textdateien finden können?
ctrl-alt-delor
@ Richard I kommen eine Ära bilden , wo wir Dateien gemeint genannt als Klartext interpretiert werden Klartext , und alle anderen Dateien (Bilder, Word - Dokumente verarbeiten, etc.) binär. Ich weiß, es ist alles nur Einsen und Nullen unter der Haube :)
Alan Storm
1
Ah, ich verstehe, was Sie mit meinen Begriffen meinen - ich werde in Zukunft Binär / Text verwenden, um Verwirrung zu vermeiden. Betreff: die Sache \ r \ n - ich verstehe, das sind die ASCII-Zeichen für den Zeilenumbruch (an den Zeilenanfang gehen) und den Zeilenvorschub (eine Zeile nach unten gehen). \ R \ n ist also ein "genaueres" Modell der realen Welt, für die ein Zeilenende-Zeichen gedacht war. Vor OS X verwendeten Macs nur ein \ r dafür. Normalerweise schreibe ich das Ganze als "willkürliche Entscheidungen, mit denen wir noch zu tun haben" ab
Alan Storm

Antworten:

20

Ich würde filedie Ausgabe verwenden und in grep oder awk umleiten, um Textdateien zu finden, dann nur den Dateinamen-Teil der fileAusgabe extrahieren und das in xargs umleiten.

so etwas wie:

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

Beachten Sie, dass das grep nach "ASCII-Text" und nicht nach "Text" sucht - Sie möchten wahrscheinlich nicht mit Rich-Text-Dokumenten, Unicode-Textdateien usw. herumspielen.

Sie können auch find(oder was auch immer) verwenden, um eine Liste von Dateien zu erstellen, mit denen Sie Folgendes überprüfen können file:

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

Das -d'\n'Argument für xargs veranlasst xargs, jede Eingabezeile als separates Argument zu behandeln, wodurch Dateinamen mit Leerzeichen und anderen problematischen Zeichen berücksichtigt werden. Das heißt, es ist eine Alternative, xargs -0wenn die Eingabequelle keine durch NULL getrennte Ausgabe erzeugt oder erzeugen kann (wie z. B. finddie -print0Option 's' ). Laut dem Changelog hat xargs im September 2005 die Option -d/ erhalten --delimiter, sollte also in keiner nicht-alten Linux-Distribution verfügbar sein (ich war mir nicht sicher, weshalb ich das überprüft habe - ich habe mich nur vage daran erinnert, dass es eine "aktuelle" Erweiterung war).

Beachten Sie, dass ein Zeilenvorschub ein gültiges Zeichen in Dateinamen ist. Dies wird also unterbrochen, wenn Dateinamen Zeilenvorschübe enthalten. Für typische Unix-Benutzer ist dies pathologisch verrückt, aber es ist nicht ungewöhnlich, ob die Dateien von Mac- oder Windows-Computern stammen.

Beachten Sie auch, dass dies filenicht perfekt ist. Es ist sehr gut darin, den Datentyp in einer Datei zu erkennen, kann aber gelegentlich verwirrt werden.

Ich habe in der Vergangenheit viele Variationen dieser Methode mit Erfolg angewendet.

cas
quelle
1
Danke für diese Lösung! Aus irgendeinem Grund filewird dies English textnicht ASCII textauf meinem Solaris-System angezeigt , daher habe ich diesen Teil entsprechend geändert. Auch habe ich awk -F: '{print $1}'mit dem Äquivalent ersetzt cut -f1 -d:.
Andrew Cheong
3
Es lohnt sich zu sagen, grep -IFilter Binärdateien
Xenoterracide
Nach dem Wort textzu suchen sollte ausreichen. Dies wird auch fileBeschreibungen wie ASCII Java program textoder HTML document textoder aufnehmen troff or preprocessor input text.
user1024
Meine Antwort ist teilweise eine Antwort / Verbesserung dieser Antwort. Sehr guter Punkt zum Greifen, um ASCII textzu vermeiden, dass RTFs durcheinander gebracht werden.
Wildcard
1
Xenoterracide: Du hast mein Leben gerettet, Mann! Nur eine Flagge -I und BINGO
Sergio Abreu
9

Nein. An einer binären oder nicht-binären Datei ist nichts Besonderes. Sie können Heuristiken wie 'enthält nur Zeichen in 0x01–0x7F' verwenden, aber das ruft Textdateien mit Nicht-ASCII-Zeichen als Binärdateien und unglückliche Binärdateien als Textdateien auf.

Nun, wenn Sie das einmal ignoriert haben ...

zip-Dateien

Wenn es von Ihrem Windows-Benutzer als ZIP-Datei stammt, unterstützt das ZIP-Format das Markieren von Dateien als Binärdatei oder als Text im Archiv. Sie können die -aOption von unzip verwenden , um darauf zu achten und zu konvertieren. Im ersten Absatz erfahren Sie natürlich, warum dies möglicherweise keine gute Idee ist (das Zip-Programm hat möglicherweise bei der Erstellung des Archivs einen Fehler vermutet).

zipinfo teilt Ihnen mit, welche Dateien in seiner zip-Datei-Liste binär (b) oder text (t) sind.

andere Dateien

Der Befehl file überprüft eine Datei und versucht, sie zu identifizieren. Insbesondere werden Sie wahrscheinlich die -iOption (Ausgabe-MIME-Typ) nützlich finden. konvertiere nur Dateien mit dem Typ text / *

derobert
quelle
6

Eine allgemeine Lösung für nur Prozess nicht binäre Dateien in bashVerwendung 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)

Ich kontaktierte den Autor des Datei - Dienstprogramm , und er hat einen geschickten -00Paramter in Version 5.26 (2016.04.16 freigegeben wird , wird zum Beispiel in der aktuellen Arch und Ubuntu 16.10) , den Druck file\0result\0für mehrere Dateien ihn zugeführten sofort, auf diese Weise Sie tun können z.B:

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

(Der awkTeil besteht darin, jede Datei herauszufiltern, die nicht binär ist. ORSIst das Ausgabetrennzeichen.)

Kann natürlich auch in einer Schleife verwendet werden:

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

Basierend auf diesem und dem vorherigen bashSkript habe ich ein kleines Skript zum Herausfiltern von Binärdateien erstellt, das die neue Methode unter Verwendung des -00Parameters von filein neueren Versionen verwendet und bei älteren Versionen auf die vorherige Methode zurückgreift:

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

Oder hier ein POSIX-y, aber es erfordert Unterstützung für 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
phk
quelle
6

Die akzeptierte Antwort hat nicht alle für mich gefunden. Hier ist ein Beispiel für die Verwendung von greps -I, um Binärdateien zu ignorieren und alle versteckten Dateien zu ignorieren ...

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

Hier wird es in einer praktischen Anwendung eingesetzt: dos2unix

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

phyatt
quelle
4

Die Antwort von Cas ist gut, setzt aber vernünftige Dateinamen voraus . Insbesondere wird davon ausgegangen, dass Dateinamen keine Zeilenumbrüche enthalten.

Es gibt keinen guten Grund, diese Annahme hier zu treffen, da es ziemlich einfach (und meiner Meinung nach sogar sauberer) ist, diesen Fall auch richtig zu behandeln:

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

Der findBefehl verwendet nur von POSIX angegebene Funktionen . Das -execAusführen von beliebigen Befehlen als boolesche Tests ist einfach, robust (behandelt ungerade Dateinamen korrekt) und portabler als -print0.

Tatsächlich werden alle Teile des Befehls mit Ausnahme von POSIX angegeben flip.

Beachten Sie, dass filedie Genauigkeit der zurückgegebenen Ergebnisse nicht garantiert wird. In der Praxis ist es jedoch sehr zuverlässig, in der Ausgabe nach "ASCII-Text" zu suchen.

(Möglicherweise fehlen einige Textdateien, es ist jedoch sehr unwahrscheinlich, dass eine Binärdatei fälschlicherweise als "ASCII-Text" identifiziert und unkenntlich gemacht wird. Wir sind also vorsichtig.)

Platzhalter
quelle
Datei callsohne Argumente kann sehr langsam sein, z. B. für Videos, die Ihnen alles über die Codierung erzählen.
6.
Außerdem gehen Sie davon aus, dass keine Datei mit beginnt -.
6.
Und ich sehe keinen Grund, warum Sie nicht einfach einen einzigen Anruf tätigen würden file, es können mehrere Dateien als Argumente verwendet werden.
6.
@phk, um auf Ihre Kommentare einzugehen: (1) Es ist gut, die potenzielle Langsamkeit zu kennen, aber ich sehe keinen POSIX-Weg, um dies zu verhindern. (2) Ich mache keine Annahmen über Dateinamen, da der findBefehl ./jedem Dateinamen vorangestellt wird , der an den Shell-Befehl übergeben wird. (3) Die gleichzeitige Verwendung grepals Test für eine einzelne fileBefehlsausgabe ist die einzige POSIX-Methode, die ich erkennen kann, um den korrekten Umgang mit Dateinamen zu gewährleisten, die möglicherweise Zeilenumbrüche enthalten.
Wildcard
Ich habe mir Ihre endgültige "POSIX-y" -Lösung angesehen und finde sie clever - aber Sie gehen davon aus, dass sie filedas --mime-encodingFlag und das --Trennzeichen unterstützt, was von POSIX nicht garantiert wird .
Wildcard
2
find . -type f -exec grep -I -q . {} \; -print

Dies findet alle regulären Dateien ( -type f) im aktuellen Verzeichnis (oder darunter), die grepfür nicht leer und nicht binär gehalten werden.

Es wird verwendet grep -I, um zwischen binären und nicht-binären Dateien zu unterscheiden. Das -IFlag und veranlassen grepdas Beenden mit einem Beendigungsstatus ungleich Null, wenn festgestellt wird, dass eine Datei binär ist. Eine "binäre" Datei ist demnach grepeine Datei, die Zeichen außerhalb des druckbaren ASCII-Bereichs enthält.

Die -qOption to grepbewirkt, dass das Programm mit dem Status 0 beendet wird, wenn das angegebene Muster gefunden wird, ohne dass Daten gesendet werden. Das von uns verwendete Muster ist ein einzelner Punkt, der mit einem beliebigen Zeichen übereinstimmt.

Wenn festgestellt wird, dass die Datei nicht binär ist und mindestens ein Zeichen enthält, wird der Name der Datei gedruckt.

Wenn Sie sich mutig fühlen, können Sie flip -ues auch anschließen:

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

Versuche dies :

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

Wo das Argument grep '[^ -~]'heißt '[^<tab><space>-~]'.

Wenn Sie es in einer Shell-Befehlszeile eingeben, geben Sie vorher Ctrl+ ein . In einem Editor sollte es kein Problem geben.VTab

  • '[^<tab><space>-~]'stimmt mit jedem Zeichen überein, das kein ASCII-Text ist (Zeilenumbrüche werden von ignoriert grep).
  • -L druckt nur den Dateinamen von Dateien, die nicht übereinstimmen
  • -Zgibt Dateinamen aus, die durch ein Nullzeichen (für xargs -0) getrennt sind
Vouze
quelle
Es ist erwähnenswert, dass mit Perl-like Regex grep -P(sofern verfügbar) \tverfügbar ist. Alternativ können Sie die Gebietsschema-Übersetzung verwenden, wenn die Shell dies unterstützt: $'\t'( bashand zshdo).
Phk
1

Alternative Lösung:

Der Befehl dos2unix konvertiert Zeilenenden von Windows CRLF nach Unix LF und überspringt automatisch Binärdateien. Ich wende es rekursiv an mit:

find . -type f -exec dos2unix {} \;
Funke
quelle
Da dos2unixmehrere Dateinamen als Argument verwendet werden können, ist dies weitaus effizienterfind . -type f -exec dos2unix {} +
Anthon,
0

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

i. (-Typ f -und -Pfad '* / git / *' -iname 'README'): Sucht nach Dateien in einem Pfad, der den Namen git und die Datei mit dem Namen README enthält. Wenn Sie einen bestimmten Ordner und Dateinamen kennen, ist die Suche hilfreich.

Der Befehl ii.-exec führt einen Befehl für den von find generierten Dateinamen aus

iii. \; Zeigt das Ende des Befehls an

iv. {} ist die Ausgabe des Datei- / Ordnernamens, der bei der vorherigen Suche gefunden wurde

v. Mehrere Befehle können anschließend ausgeführt werden. Durch Anhängen von -exec "command" \; wie mit -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. 

Sie können dieses Testverzeichnis klonen und ausprobieren: https://github.com/alphaCTzo7G/stackexchange/tree/master/linux/findSolution204092017

Ausführlichere Antworten finden Sie hier: https://github.com/alphaCTzo7G/stackexchange/blob/master/linux/findSolution204092017/README.md

alpha_989
quelle