it-swarm.com.de

Alle "nicht-binären" Dateien finden

Ist es möglich, mit dem Befehl find 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 Befehlszeilenprogramm (flip -u) das wird die Zeilenenden zwischen * nix und Windows umdrehen. Also würde ich gerne so etwas machen

find . -type f | xargs flip -u

Wenn dieser Befehl jedoch für eine Bilddatei oder eine andere binäre Mediendatei ausgeführt wird, wird die Datei beschädigt. Mir ist klar, dass ich damit eine Liste von Dateierweiterungen erstellen und filtern könnte, aber ich hätte lieber etwas, das nicht davon abhängt, 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?

46
Alan Storm

Ich würde file verwenden und die Ausgabe in grep oder awk leiten, um Textdateien zu finden, dann nur den Dateinamen-Teil der Ausgabe von file extrahieren und diesen in xargs leiten.

etwas wie:

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

Beachten Sie, dass der grep eher nach 'ASCII-Text' als nur nach 'Text' sucht - Sie möchten wahrscheinlich nicht mit Rich-Text-Dokumenten oder Unicode-Textdateien usw. herumspielen.

Sie können auch find (oder was auch immer) verwenden, um eine Liste von Dateien zu erstellen, die mit file untersucht werden sollen:

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 bewirkt, dass xargs jede Eingabezeile als separates Argument behandelt und somit Dateinamen mit Leerzeichen und anderen problematischen Zeichen berücksichtigt. d.h. es ist eine Alternative zu xargs -0 wenn die Eingabequelle keine NULL-getrennte Ausgabe generiert oder nicht generieren kann (z. B. find 's -print0 Möglichkeit). Laut dem Changelog hat xargs das -d/--delimiter Option im September 2005 sollte also in jeder nicht alten Linux-Distribution sein (ich war mir nicht sicher, weshalb ich es überprüft habe - ich erinnerte mich nur vage daran, dass es eine "neue" Ergänzung 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 file nicht 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 viele Male mit Erfolg verwendet.

21
cas

Nein. Eine binäre oder nicht-binäre Datei hat nichts Besonderes. Sie können Heuristiken wie "enthält nur Zeichen in 0x01–0x7F" verwenden, die jedoch Textdateien mit Nicht-ASCII-Zeichen-Binärdateien und unglückliche Binärdateien als Textdateien aufrufen.

Nun, wenn Sie das 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är- oder Textdateien im Archiv. Sie können die Option -a Von unzip verwenden, um dies zu beachten und zu konvertieren. Im ersten Absatz erfahren Sie natürlich, warum dies möglicherweise keine gute Idee ist (das Zip-Programm hat möglicherweise falsch geraten, als es das Archiv erstellt hat).

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

andere Dateien

Der Befehl file betrachtet eine Datei und versucht, sie zu identifizieren. Insbesondere wird die Option -i (Ausgabe-MIME-Typ) wahrscheinlich nützlich sein. Konvertieren Sie nur Dateien mit dem Typ text/*

9
derobert

Die akzeptierte Antwort fand nicht alle für mich. Hier ist ein Beispiel mit 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/11219

8
phyatt

Eine allgemeine Lösung, um nicht-binäre Dateien in bash nur mit file -b --mime-encoding Zu verarbeiten:

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 habe den Autor des Dienstprogramms Datei kontaktiert und er hat in Version 5.26 (veröffentlicht am 16.04.2016, z. B. im aktuellen Arch und Ubuntu 16.10) einen raffinierten -00 - Parameter hinzugefügt, der file\0result\0 Für mehrere Dateien, die gleichzeitig eingespeist werden, können Sie auf folgende Weise Folgendes tun:

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

(Der Teil awk dient zum Herausfiltern jeder Datei, die nicht nicht binär ist. ORS ist 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 habe ich ein kleines bash Skript zum Herausfiltern von Binärdateien erstellt, das die neue Methode unter Verwendung des Parameters -00 Von file in neueren Versionen verwendet und fällt Zurück zur vorherigen Methode bei älteren Versionen:

#!/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 -- "[email protected]" |
    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 -- "[email protected]" |
    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
7
phk
find . -type f -exec grep -I -q . {} \; -print

Dadurch werden alle regulären Dateien gefunden (-type f) im aktuellen Verzeichnis (oder darunter), das grep für nicht leer und nicht binär hält.

Es verwendet grep -I zur Unterscheidung zwischen binären und nicht-binären Dateien. Das -I flag und bewirkt, dass grep mit einem Exit-Status ungleich Null beendet wird, wenn festgestellt wird, dass eine Datei binär ist. Eine "binäre" Datei ist gemäß grep eine Datei, die Zeichen außerhalb des druckbaren Bereichs ASCII) enthält.

Das -q Option auf grep bewirkt, dass es mit einem Exit-Status von Null beendet wird, wenn das angegebene Muster gefunden wird, ohne Daten auszugeben. Das Muster, das wir verwenden, ist ein einzelner Punkt, der mit jedem 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 Ihr flip -u auch hinein:

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

Cas's Antwort ist gut, aber es werden vernünftige Dateinamen angenommen; Insbesondere wird davon ausgegangen, dass Dateinamen keine Zeilenumbrüche enthalten.

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

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

Der Befehl find verwendet nur von POSIX angegebene Funktionen . Verwenden von -exec beliebige Befehle als boolesche Tests auszuführen ist einfach, robust (behandelt ungerade Dateinamen korrekt) und portabler als -print0.

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

Beachten Sie, dass file keine Garantie für die Richtigkeit der zurückgegebenen Ergebnisse garantiert. In der Praxis ist das Suchen nach "ASCII-Text" in der Ausgabe jedoch recht zuverlässig.

(Möglicherweise fehlen einige Textdateien , aber es ist sehr unwahrscheinlich, dass eine Binärdatei fälschlicherweise als "ASCII-Text" identifiziert und entstellt wird - also sind wir es auf der Seite der Vorsicht irren.)

4
Wildcard

Versuche dies :

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

Wo das Argument von grep '[^ -~]' ist '[^<tab><space>-~]'.

Wenn Sie es in eine Shell-Befehlszeile eingeben, geben Sie ein Ctrl+V Vor Tab. In einem Editor sollte es kein Problem geben.

  • '[^<tab><space>-~]' stimmt mit jedem Zeichen überein, das nicht ASCII text) ist (Wagenrückläufe werden von grep ignoriert).
  • -L druckt nur den Dateinamen von Dateien, die nicht übereinstimmen
  • -Z gibt Dateinamen aus, die durch ein Nullzeichen getrennt sind (für xargs -0)
1
Vouze

Alternative Lösung:

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

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

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

i. (-type f -und -path '*/ git /*' -iname 'README'): Sucht nach Dateien in einem Pfad, der das enthält Name Git und Datei mit dem Namen README. Wenn Sie einen bestimmten Ordner und Dateinamen kennen, nach dem Sie suchen möchten, ist dies 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 der Datei/des Ordnernamens, die bei der vorherigen Suchsuche gefunden wurde

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

detailliertere Antwort hier: https://github.com/alphaCTzo7G/stackexchange/blob/master/linux/findSolution204092017/README.md

0
alpha_989