Linux-Befehl: Wie finde ich nur Textdateien?

100

Nach ein paar Suchanfragen von Google habe ich Folgendes gefunden:

find my_folder -type f -exec grep -l "needle text" {} \; -exec file {} \; | grep text

Das ist sehr unhandlich und gibt nicht benötigte Texte wie MIME-Typ-Informationen aus. Irgendwelche besseren Lösungen? Ich habe viele Bilder und andere Binärdateien im selben Ordner mit vielen Textdateien, die ich durchsuchen muss.

datasn.io
quelle

Antworten:

184

Ich weiß, dass dies ein alter Thread ist, aber ich bin darauf gestoßen und dachte, ich würde meine Methode teilen, die sich als sehr schnelle Methode herausgestellt hat find, um nur nicht-binäre Dateien zu finden:

find . -type f -exec grep -Iq . {} \; -print

Die -IOption zu grep weist es an, Binärdateien sofort zu ignorieren, und die .Option zusammen mit -qmacht es, dass es sofort mit Textdateien übereinstimmt, so dass es sehr schnell geht. Sie können das -printin a -print0für Rohrleitungen in ein xargs -0oder etwas ändern , wenn Sie sich Gedanken über Leerzeichen machen (danke für den Tipp, @ lucas.werkmeister!)

Der erste Punkt ist auch nur für bestimmte BSD-Versionen findwie z. B. unter OS X erforderlich , aber es schadet nichts, wenn Sie ihn immer dort haben, wenn Sie ihn in einen Alias ​​oder etwas anderes einfügen möchten.

EDIT : Wie @ruslan richtig ausgeführt hat, der -andkann verzichtet werden , da es impliziert.

Crudcore
quelle
16
Unter Mac OS X muss ich dies ändern find . -type f -exec grep -Il "" {} \;.
Alec Jacobson
3
Dies ist besser als Peoros Antwort, weil 1. es tatsächlich die Frage beantwortet 2. es keine falsch positiven
Ergebnisse
3
Sie können auch verwenden, find -type f -exec grep -Iq . {} \; -and -printwas den Vorteil hat, dass es die Dateien in behält find; Sie können -printdurch eine andere ersetzen -exec, die nur für Textdateien ausgeführt wird. (Wenn Sie grepdie Dateinamen drucken lassen , können Sie Dateinamen nicht mit Zeilenumbrüchen unterscheiden.)
Lucas Werkmeister
1
@ NathanS.Watson-Haigh Das sollte nicht sein, da es sofort mit Textdateien übereinstimmen sollte. Haben Sie einen bestimmten Anwendungsfall, den Sie teilen können?
Crudcore
2
find . -type f -exec grep -Il . {} +ist viel schneller. Nachteil ist, dass es nicht von einem anderen erweitert werden kann, -execwie @ lucas.werkmeister vorgeschlagen hat
Henning
11

Basierend auf dieser SO-Frage :

grep -rIl "needle text" my_folder

Crayzeewulf
quelle
Danke, das -Iist ein Lebensretter.
Dominique
10

Warum ist es unhandlich? Wenn Sie es häufig verwenden müssen und es nicht jedes Mal eingeben möchten, definieren Sie einfach eine Bash-Funktion dafür:

function findTextInAsciiFiles {
    # usage: findTextInAsciiFiles DIRECTORY NEEDLE_TEXT
    find "$1" -type f -exec grep -l "$2" {} \; -exec file {} \; | grep text
}

lege es in dein .bashrcund renne dann einfach:

findTextInAsciiFiles your_folder "needle text"

wann immer du willst.


BEARBEITEN , um die Bearbeitung von OP widerzuspiegeln:

Wenn Sie MIME-Informationen ausschneiden möchten, können Sie der Pipeline einfach eine weitere Stufe hinzufügen, in der MIME-Informationen herausgefiltert werden. Dies sollte es tun, indem sie nur das, was kommt vor :: cut -d':' -f1:

function findTextInAsciiFiles {
    # usage: findTextInAsciiFiles DIRECTORY NEEDLE_TEXT
    find "$1" -type f -exec grep -l "$2" {} \; -exec file {} \; | grep text | cut -d ':' -f1
}
Peoro
quelle
Ich bin mir nicht sicher, ob "grep text" genau genug ist, um genau alle Textdateien abzurufen. Ich meine, gibt es Textdateitypen, die keinen 'Text' in der Zeichenfolge ihrer MIME-Typbeschreibung enthalten?
Datasn.io
@ kavoir.com: ja. Aus dem fileHandbuch: "Benutzer müssen wissen, dass auf allen lesbaren Dateien in einem Verzeichnis das Wort 'Text' gedruckt ist."
Peoro
2
Wäre es nicht ein bisschen klüger, vor dem Greppen nach Textdateien zu suchen, anstatt nach Textdateien zu greifen und sie dann herauszufiltern?
Benutzer unbekannt
/proc/meminfo, /proc/cpuinfoUsw. sind Textdateien, aber file /proc/meminfosagt /proc/meminfo: empty. Ich frage mich, ob "leer" zusätzlich zu "Text" getestet werden sollte, bin mir aber nicht sicher, ob auch andere Typen "leer" melden könnten.
Timo Kähkönen
"Warum ist es unhandlich?" - "gibt nicht benötigte Texte aus". Diese Antwort lässt das nicht zu.
user123444555621
4
find . -type f -print0 | xargs -0 file | grep -P text | cut -d: -f1 | xargs grep -Pil "search"

Dies ist leider nicht platzsparend. Das Einfügen in ein Bash-Skript macht es ein bisschen einfacher.

Das ist platzsparend:

#!/bin/bash
#if [ ! "$1" ] ; then
    echo "Usage: $0 <search>";
    exit
fi

find . -type f -print0 \
  | xargs -0 file \
  | grep -P text \
  | cut -d: -f1 \
  | xargs -i% grep -Pil "$1" "%"
Antti Rytsölä
quelle
2
In Ihrem Skript gibt es einige Probleme: 1. Was passiert, wenn eine Binärdatei benannt wird text.bin? 2. Was ist, wenn ein Dateiname a enthält :?
Thkala
3

Ein anderer Weg, dies zu tun:

# find . |xargs file {} \; |grep "ASCII text"

Wenn Sie auch leere Dateien möchten:

#  find . |xargs file {} \; |egrep "ASCII text|empty"
Der IT-Typ
quelle
2

Wie wäre es damit:

$ grep -rl "needle text" my_folder | tr '\n' '\0' | xargs -r -0 file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable'

Wenn Sie die Dateinamen ohne Dateityp verwenden möchten, fügen Sie einfach einen endgültigen sedFilter hinzu.

$ grep -rl "needle text" my_folder | tr '\n' '\0' | xargs -r -0 file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable' | sed 's|:[^:]*$||'

Sie können nicht benötigte Dateitypen herausfiltern, indem Sie -e 'type'dem letzten grepBefehl weitere Optionen hinzufügen .

BEARBEITEN:

Wenn Ihre xargsVersion diese -dOption unterstützt , werden die obigen Befehle einfacher:

$ grep -rl "needle text" my_folder | xargs -d '\n' -r file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable' | sed 's|:[^:]*$||'
thkala
quelle
wie dumm von mir. Rekursives Grep nicht bemerkt. Wie ich verstanden habe, ist es eigentlich ziemlich schnell, obwohl es in vielen Anwendungen etwas eingeschränkt ist. +1 für dich.
Antti Rytsölä
2

So habe ich es gemacht ...

1. Erstellen Sie ein kleines Skript, um zu testen, ob eine Datei Klartext ist.

#!/bin/bash
[[ "$(file -bi $1)" == *"file"* ]]

2. benutze find wie vorher

find . -type f -exec istext {} \; -exec grep -nHi mystring {} \;
Robert
quelle
Ich denke du meinst == *"text"* ]]?
Benutzer unbekannt
Sie können stattdessen den Match-Operator `= ~" text "]]` verwenden.
Benutzer unbekannt
2

Ich habe zwei Probleme mit der Antwort von histumness:

  • Es werden nur Textdateien aufgelistet. Sie werden nicht wie gewünscht durchsucht. Verwenden Sie, um tatsächlich zu suchen

    find . -type f -exec grep -Iq . {} \; -and -print0 | xargs -0 grep "needle text"
    
  • Es erzeugt einen Grep-Prozess für jede Datei, der sehr langsam ist. Eine bessere Lösung ist dann

    find . -type f -print0 | xargs -0 grep -IZl . | xargs -0 grep "needle text"
    

    oder einfach

    find . -type f -print0 | xargs -0 grep -I "needle text"
    

    Dies dauert nur 0,2 Sekunden im Vergleich zu 4 Sekunden für die obige Lösung (2,5 GB Daten / 7700 Dateien), dh 20- mal schneller .

Auch niemand zitierte ag, den Silver Searcher oder ack-grep als Alternativen. Wenn eine davon verfügbar ist, sind sie viel bessere Alternativen:

ag -t "needle text"    # Much faster than ack
ack -t "needle text"   # or ack-grep

Achten Sie als letzte Anmerkung auf Fehlalarme (Binärdateien als Textdateien). Ich hatte bereits falsch positiv mit grep / ag / ack, also liste die übereinstimmenden Dateien besser zuerst auf, bevor du die Dateien bearbeitest.

fuujuhi
quelle
1

Obwohl es sich um eine alte Frage handelt, denke ich, dass dieser Infobild die Qualität der Antworten hier verbessern wird.

Wenn ich Dateien mit gesetztem ausführbaren Bit ignoriere , verwende ich einfach diesen Befehl:

find . ! -perm -111

Um zu verhindern, dass es rekursiv in andere Verzeichnisse eingegeben wird:

find . -maxdepth 1 ! -perm -111

Keine Notwendigkeit für Rohre viele Befehle, nur den mächtigen Ebene zu mischen find Befehl.

  • Haftungsausschluss: Es ist nicht genau das , was OP gefragt hat, da es nicht prüft, ob die Datei binär ist oder nicht. Es werden beispielsweise Bash-Skriptdateien herausgefiltert , die selbst Text sind, aber das ausführbare Bit gesetzt haben .

Trotzdem hoffe ich, dass dies für jeden nützlich ist.

Dr. Beco
quelle
0

Ich mache es so: 1) Da es zu viele Dateien (~ 30k) gibt, um sie zu durchsuchen, generiere ich täglich die Textdateiliste für die Verwendung über crontab mit dem folgenden Befehl:

find /to/src/folder -type f -exec file {} \; | grep text | cut -d: -f1 > ~/.src_list &

2) Erstellen Sie eine Funktion in .bashrc:

findex() {
    cat ~/.src_list | xargs grep "$*" 2>/dev/null
}

Dann kann ich den folgenden Befehl verwenden, um die Suche durchzuführen:

findex "needle text"

HTH :)

Frank Fang
quelle
0

Ich bevorzuge Xargs

find . -type f | xargs grep -I "needle text"

Wenn Ihre Dateinamen seltsam sind, suchen Sie mit den Optionen -0 nach:

find . -type f -print0 | xargs -0 grep -I "needle text"
dalore
quelle
0
  • Bash-Beispiel zum Serachieren von Text "eth0" in / etc in allen Text- / ASCII-Dateien

grep eth0 $ (find / etc / -type f -exec Datei {} \; | egrep -i "text | ascii" | cut -d ':' -f1)

Gabriel G.
quelle
0

Hier ist eine vereinfachte Version mit erweiterten Erklärungen für Anfänger wie mich, die lernen möchten, wie man mehr als einen Befehl in eine Zeile setzt.

Wenn Sie das Problem schrittweise aufschreiben würden, würde es folgendermaßen aussehen:

// For every file in this directory
// Check the filetype
// If it's an ASCII file, then print out the filename

Um dies zu erreichen, können wir drei UNIX - Befehle zur Verfügung : find, file, und grep.

find überprüft jede Datei im Verzeichnis.

filewird uns den Dateityp geben. In unserem Fall suchen wir nach einer Rückgabe von 'ASCII-Text'

grep sucht in der Ausgabe von nach dem Schlüsselwort 'ASCII' file

Wie können wir diese also in einer einzigen Zeile aneinander reihen? Es gibt mehrere Möglichkeiten, dies zu tun, aber ich finde, dass es am sinnvollsten ist, dies in der Reihenfolge unseres Pseudocodes zu tun (insbesondere für Anfänger wie mich).

find ./ -exec file {} ";" | grep 'ASCII'

Sieht kompliziert aus, ist aber nicht schlecht, wenn wir es aufschlüsseln:

find ./= Durchsuche jede Datei in diesem Verzeichnis. Der findBefehl druckt den Dateinamen einer Datei aus, die dem 'Ausdruck' entspricht, oder was auch immer nach dem Pfad steht, in unserem Fall das aktuelle Verzeichnis oder./

Das Wichtigste zu verstehen ist, dass alles nach diesem ersten Bit entweder als wahr oder falsch bewertet wird. Wenn True, wird der Dateiname ausgedruckt. Wenn nicht, wird der Befehl fortgesetzt.

-exec= Dieses Flag ist eine Option innerhalb des Befehls find, mit der wir das Ergebnis eines anderen Befehls als Suchausdruck verwenden können. Es ist wie das Aufrufen einer Funktion innerhalb einer Funktion.

file {}= der Befehl, der innerhalb von aufgerufen wird find. Der fileBefehl gibt eine Zeichenfolge zurück, die den Dateityp einer Datei angibt. Regelmäßig würde es so aussehen : file mytextfile.txt. In unserem Fall möchten wir, dass die Datei verwendet wird, die vom findBefehl angezeigt wird. Daher setzen wir die geschweiften Klammern ein {}, um als leere Variable oder Parameter zu fungieren. Mit anderen Worten, wir fordern das System lediglich auf, für jede Datei im Verzeichnis eine Zeichenfolge auszugeben.

";"= Dies wird von findund ist das Interpunktionszeichen am Ende unseres -execBefehls. Weitere Informationen finden Sie im Handbuch für 'Suchen', wenn Sie es benötigen, indem Sie es ausführen man find.

| grep 'ASCII'= |ist eine Pfeife. Pipe nimmt die Ausgabe von allem, was links ist, und verwendet sie als Eingabe für alles, was rechts ist. Es nimmt die Ausgabe des findBefehls (eine Zeichenfolge, die der Dateityp einer einzelnen Datei ist) und testet sie, um festzustellen, ob sie die Zeichenfolge enthält 'ASCII'. Wenn dies der Fall ist, wird true zurückgegeben.

JETZT gibt der Ausdruck rechts von find ./true zurück, wenn der grepBefehl true zurückgibt. Voila.

Mepler
quelle
0

Wenn Sie daran interessiert sind, einen Dateityp anhand seiner magischen Bytes mithilfe des fantastischen fileDienstprogramms in Kombination mit der Leistung von zu finden find, kann dies nützlich sein:

$ # Let's make some test files
$ mkdir ASCII-finder
$ cd ASCII-finder
$ dd if=/dev/urandom of=binary.file bs=1M count=1
1+0 records in
1+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.009023 s, 116 MB/s
$ file binary.file
binary.file: data
$ echo 123 > text.txt
$ # Let the magic begin
$ find -type f -print0 | \
    xargs -0 -I @@ bash -c 'file "$@" | grep ASCII &>/dev/null && echo "file is ASCII: $@"' -- @@

Ausgabe:

file is ASCII: ./text.txt

Legende: $ist die interaktive Shell-Eingabeaufforderung, in die wir unsere Befehle eingeben

Sie können das Teil nachträglich ändern &&, um ein anderes Skript aufzurufen oder andere Dinge inline auszuführen. Wenn diese Datei eine bestimmte Zeichenfolge enthält, können Sie die gesamte Datei katzen oder nach einer sekundären Zeichenfolge suchen.

Erläuterung:

  • find Elemente, die Dateien sind
  • Stellen Sie xargssicher, dass jedes Element als Zeile in einen Liner- bash Befehl / ein Skript eingefügt wird
  • fileÜberprüft den Dateityp anhand eines magischen Bytes und grepprüft, ob ASCII vorhanden ist. Wenn ja, wird &&der nächste Befehl ausgeführt.
  • findDruckt die Ergebnisse nullgetrennt. Dies ist gut, um Dateinamen mit Leerzeichen und Metazeichen zu umgehen.
  • xargsMit der -0Option werden sie nullgetrennt gelesen , -I @@ jeder Datensatz wird verwendet und als Positionsparameter / Argument für das Bash-Skript verwendet.
  • --for bashstellt sicher, dass alles, was danach kommt, ein Argument ist, auch wenn es mit -like beginnt, -cdas sonst als Bash-Option interpretiert werden könnte

Wenn Sie andere Typen als ASCII suchen müssen, ersetzen Sie sie einfach durch einen grep ASCIIanderen Typ, zgrep "PDF document, version 1.4"

sdkks
quelle
-1
find . -type f | xargs file | grep "ASCII text" | awk -F: '{print $1}'

Verwenden Sie den Befehl find, um alle Dateien aufzulisten, verwenden Sie den Befehl file, um zu überprüfen, ob es sich um Text handelt (nicht tar, key), und verwenden Sie schließlich den Befehl awk, um das Ergebnis zu filtern und zu drucken.

Roy Zeng
quelle
-4

Wie wäre es damit

 find . -type f|xargs grep "needle text"
Navi
quelle
Dies sucht nicht"needle text"
Peoro
@ Navi: Das Beispiel OP findet nur Dateien mit"needl text"
Peoro
3
@Navi: Jetzt sucht es nicht mehr nach Textdateien: Wenn eine Binärdatei diese enthält, wird "needle text"sie gefunden
Peoro
Warum höre ich dir überhaupt zu?
Navi
1
@ Navi: Ihr Einzeiler überprüft keine Dateitypen und hat auch große Probleme mit Leerzeichen in Dateinamen ...
Thkala