
Ich versuche, ein bash
Skript zu schreiben, das den Inhalt von Dateien in einem angegebenen Verzeichnisbaum nach dem Vorhandensein einer angegebenen Teilzeichenfolge durchsucht.
Die Verwendung grep
der rekursiven Funktion allein reicht nicht aus, da ich möglicherweise über das /
Verzeichnis (und alle Unterverzeichnisse) eines Systems iterieren muss, was zu grep
Speichermangel und Abbruch führen kann. Daher habe ich mich entschieden, eine Liste aller Verzeichnisse und Unterverzeichnisse im angegebenen Verzeichnisbaum zu erhalten, wobei ich find
die folgenden Variablen verwende, die an das Skript übergebene Argumente bezeichnen.
searchdir=$HOME # passed in a script argument
searchstr="secret" # passed in a script argument
Ich rufe das find
Dienstprogramm auf und speichere die Ausgabe in einer temporären Datei.
TF=$(mktemp)
find ${searchdir} -type d 1>$TF 2>/dev/null
Mit der Liste aller Verzeichnisse in der temporären Datei iteriere ich in einer while-do
Schleife über die Zeilen dieser Datei mit der Absicht, eine Suche über alle Dateien in jedem Verzeichnis durchzuführen. Für grep
verwende ich das Format der Parameter, das in angegeben istdiese Antwortum alle Dateien, einschließlich der versteckten, in einem einzelnen Verzeichnis zu suchen.
cat $TF | while read line || [[ -n $line ]];
do
grepdir="${line}/{*,.*}"
grep -sHn "${searchstr}" ${grepdir}
done
... dieser Code erzeugt jedoch keine Ausgabe.
Ich habe das überprüft …
Es ${TF}
enthält die korrekte Liste aller Verzeichnisse. Die Ausgabe der ${grepdir}
Variable ergibt die Ausgabe, die ich erwarte.
/home/user/{*,.*}
/home/user/.ssh/{*,.*}
/home/user/test/{*,.*}
# ... and so on
Wenn ich den grep
Befehl mit einem fest codierten Verzeichnis ausführe, insbesondere dem ~/test/
Verzeichnis, das zwei Testdateien mit der Zeichenfolge enthält, die gefunden werden soll
grep -sHn "${searchstr}" /home/user/test/{*,.*}
... es gibt die beiden Dateien korrekt aus, die die Teilzeichenfolge „geheim“ enthalten.
/home/user/test/asdf:7:secret
/home/user/test/test.txt:5:asdfasfdsecretaasdfafd
Ein Format, das für mich funktioniert, ist das ursprünglich erwähnte in derAntwort zur Diskussion der rekursiven Verwendung vongrep
. Wenn ich dies mache:
cat $TF | while read line || [[ -n $line ]];
do
grep -rn "${line}" -e "${searchstr}"
done
... Ich erhalte eine Ausgabe (technisch korrekt, aber mit vielen doppelten Einträgen), aber da grep
die Verzeichnisse rekursiv verarbeitet werden und ich eine Liste aller Verzeichnisse habe, werde ich zwangsläufig mehrmals dieselben Ergebnisse erhalten und bei Verzeichnissen wie dem oben erwähnten Stammverzeichnis grep
wird ein vollständiger Fehler auftreten, was ich jedoch vermeiden möchte.
Ich sollte wahrscheinlich auch erwähnen, dass meine verzweifelten Versuche, es zum Laufen zu bringen, wie etwa die Übergabe $(echo "${grepdir}")
als Parameter, ebenfalls zu keinen Ergebnissen führten.
Wahrscheinlich liegt ein Missverständnis in meinem Denken oder Verständnis von vor bash
. Sollte bash
die ${grepdir}
Variable nicht erweitert werden, bevor ein Aufruf von erfolgt grep
? Wo liegt in meinem Skript ein Fehler?
Antwort1
Regel Nr. 1: Wenn ein Befehl oder Skript nicht das tut, was Sie möchten,
Schauen Sie sich die Fehlermeldungen an. Werfen Sie sie nicht hinein /dev/null
.
Sie erhalten Fehlermeldungen wie
grep: /home/user/{*,.*}: No such file or directory
grep: /home/user/.ssh/{*,.*}: No such file or directory
grep: /home/user/test/{*,.*}: No such file or directory
aber Sie sehen sie nicht.
Wenn wir uns ansehenbash(1), wir sehen
Die Erweiterung wird in der Befehlszeile durchgeführt, nachdem diese in Wörter aufgeteilt wurde. Es gibt sieben Arten der Erweiterung: Klammernerweiterung, Tilde-Erweiterung, Parameter- und Variablenerweiterung, Befehlsersetzung, arithmetische Erweiterung, Wortaufteilung und Pfadnamenerweiterung.
Die Reihenfolge der Erweiterungen ist: Klammernerweiterung, Tilde-Erweiterung, Parameter- und Variablenerweiterung, arithmetische Erweiterung und Befehlsersetzung (von links nach rechts durchgeführt), Worttrennung und Pfadnamenerweiterung.
Der wichtige Teil für Ihre Situation ist, dass die Klammererweiterung vor der Variablenerweiterung erfolgt. Wenn Sie also sagen:
grep -sHn "${searchstr}" "${line}"/{*,.*}
Dann
- Die Klammererweiterung würde das letzte Token in
"${line}"/*
und verwandeln"${line}"/.*
, - Die Variablenerweiterung würde das obige in
/home/user/*
und umwandeln/home/user/.*
, und dann - Die Pfadnamenerweiterung würde das Obige in eine Liste von Dateinamen umwandeln.
Aber wenn Sie sagen
grep -sHn "${searchstr}" ${grepdir}
Dann
- Die Variablenerweiterung verwandelt das letzte Token in
/home/user/{*,.*}
,
und dann ist es für eine Klammernerweiterung zu spät.
grep
sucht nach einer Datei mit dem wörtlichen Namen /home/user/{*,.*}
.
PS
grep -sHn "${searchstr}" "${line}/{*,.*}"
würde auch nicht funktionieren, da die Anführungszeichen die Klammern- und Pfadnamenerweiterung verhindern würden.
PPS: Sie brauchen nicht alle diese Zahnspangen;
grep -sHn "$searchstr" "$line"/{*,.*}
wäre in Ordnung.
Antwort2
Der Grund, warum grep beim rekursiven Durchlaufen des gesamten Systems abbricht, liegt wahrscheinlich nicht darin, dass es mit der Datenmenge nicht zurechtkommt, sondern darin, dass es über die eine oder andere Pseudo- oder Gerätedatei in /proc, /sys oder /dev stolpert. Mit der --exclude
Option auf der Kommandozeile könnte man die betreffenden Verzeichnisse ausschließen.
Der Grund, warum die Platzhalter nicht erweitert werden, liegt darin, dass sie in dieser Zeile in Anführungszeichen stehen:
grepdir="${line}/{*,.*}"
Eine entsprechende Änderung wird wahrscheinlich dazu beitragen, dass sie erweitert werden.
grepdir="${line}/"{*,.*}
Eine andere Möglichkeit, dies zu erreichen (mit weniger Skripting Ihrerseits), besteht darin, die Dateien auszuwählen find
und die Dateipfade xargs
zur Verarbeitung weiterzuleiten:find / ... -print 0 | xargs -0 ...
In beiden Fällen würden Sie jedoch wahrscheinlich trotzdem über die Dateien stolpern, über die das ursprüngliche rekursive Grep gestolpert ist, sofern Sie sie nicht ausschließen.