Wie grep auf alle Dateien nicht rekursiv in einem Verzeichnis verwenden?

34

Ich möchte in allen Dateien eines Verzeichnisses (und nicht in seinen Unterverzeichnissen) nach einer Textzeichenfolge suchen. Ich weiß, dass die -rOption dies tut, aber das ist nicht das, was ich will.

  1. Laufen

    grep "string" /path/to/dir
    

    soll das können, habe ich gelesen, aber es gibt mir den fehler:

    grep: dir: Ist ein Verzeichnis

  2. Als nächstes habe ich versucht, grepmehrere Dateien auszuführen.

    grep "string" .bashrc .bash_aliases funktioniert perfekt.

    grep "string" .bash* funktioniert auch wie vorgesehen.

    grep "string" * gibt mir die fehler:

    grep: data: Is a directory
    grep: Desktop: Is a directory
    grep: Documents: Is a directory
    grep: Downloads: Is a directory
    ...
    

Es werden nur die Fehler gedruckt, die übereinstimmenden Zeilen werden nicht angezeigt. Ich habe versucht, die -sOption zu verwenden, aber ohne Erfolg.

Also meine Fragen:

  1. Warum kann ich grepein Verzeichnis nicht wie in (1) verwenden, wenn ich das sollte? Ich habe das an vielen Beispielen im Internet gesehen.
    Bearbeiten : Wenn ich "grep für ein Verzeichnis verwenden" sage, meine ich "in allen Dateien in diesem Verzeichnis mit Ausnahme der Unterverzeichnisse suchen". Ich glaube, dass dies das ist, was grep tut, wenn Sie ein Verzeichnis anstelle einer Datei übergeben. Irre ich mich

  2. Bitte erläutern Sie mir die Funktionsweise grep, die das Verhalten der Befehle in (2) erklären würde.
    Bearbeiten : Lassen Sie mich genauer sein. Warum funktioniert die Verwendung von Platzhaltern, um mehrere Dateien anzugeben, in denen nach gesucht werden soll, .bash*und nicht mit *oder sogar ./*?

  3. Wie kann ich alle Dateien in einem Verzeichnis (und nicht in dessen Unterverzeichnissen) durchsuchen grep?

John Red
quelle
Außerdem verlassen Sie sich auf die Shell, die Platzhalter wie " *Globbing" erweitert. Beim Globbing werden keine Dateinamen berücksichtigt, die mit einem Punkt wie .bashrcdem Standard beginnen. Sie können die Shell-Optionen so einstellen, dass sie diese Dateien enthalten, aber Sie können sich selbst ein bisschen durcheinander bringen, wenn Sie nicht wissen, was Sie tun. Eine gute Anleitung zum Verständnis des Globierens finden Sie hier mywiki.wooledge.org/glob
Arronical
Ich weiß nicht warum, aber ich habe immer nach versteckten Dateien gesucht und es hat immer funktioniert. Ich habe keine Einstellung oder etwas geändert. Wie ich in (2) gezeigt habe, funktioniert es auch mit grep "string" .bash*.
John Red
Entschuldigung, mein letztes Beispiel war falsch. Sie können auch in versteckten Dateien suchen und "is a directory" unterdrücken, da Linux Verzeichnisse technisch als einen anderen Dateityp ansieht. Der Befehl wäre dann: grep "string" * .* 2>/dev/nullodergrep -s "string" * .*
Terrance
Auch diese stackoverflow.com/q/9217185/3701431
Sergiy Kolodyazhnyy

Antworten:

40

In Bash wird ein Glob nicht in versteckte Dateien expandiert. Wenn Sie also alle Dateien in einem Verzeichnis durchsuchen möchten , müssen Sie versteckte .*und nicht versteckte Dateien angeben *.

Um die "Ist ein Verzeichnis" -Fehler zu vermeiden, könnten Sie verwenden -d skip, aber auf meinem System erhalte ich auch einen Fehler grep: .gvfs: Permission denied , daher empfehle ich die Verwendung -s, bei der alle Fehlermeldungen ausgeblendet werden.

Der gesuchte Befehl lautet also:

grep -s "string" * .*

Wenn Sie Dateien in einem anderen Verzeichnis suchen:

grep -s "string" /path/to/dir/{*,.*}

Eine weitere Option ist die Verwendung der dotglobShell-Option, mit der ein Glob versteckte Dateien einbezieht.

shopt -s dotglob
grep -s "string" *

Für Dateien in einem anderen Verzeichnis:

grep -s "string" /path/to/dir/*

† Jemand erwähnte, dass ich diesen Fehler nicht bekommen sollte. Sie mögen Recht haben - ich habe ein bisschen gelesen, aber ich konnte es mir nicht vorstellen.

wjandrea
quelle
Gibt es einen Grund für den Abstand zwischen *und .*?
Hashim
2
@Hashim Vergleichen Sie die Ausgabe von echo * .*und echo *.*führen Sie sie in Ihrem Home-Verzeichnis aus. Der Unterschied sollte offensichtlich sein. Ansonsten LMK und ich werde es erklären.
wjandrea
Interessant, echo *zeigt also nicht versteckte Dateien und Ordner, echo *.*zeigt nicht versteckte Dateien, echo .*zeigt alle Dateien und echo * .*zeigt alle Dateien und Verzeichnisse. Aber warum der Grund für den Abstand zwischen den beiden im letzteren Fall? Es fühlt sich für mich unordentlich an. Gibt es keine Möglichkeit, beide zu kombinieren, um die gleichen Ergebnisse zu erzielen? Oder gibt es sonst eine syntaktische Erklärung, warum die beiden hier getrennt werden müssen, oder handelt es sich um * .*einen Ausnahmefall?
Hashim
1
@Hashim Ich bin mir nicht sicher, wie Sie zu diesen Schlussfolgerungen gekommen sind. Lassen Sie mich das erklären. Erstens sind Verzeichnisse in diesem Kontext Dateien. Stellt in Globs *alle nicht ausgeblendeten Dateien dar (dh Dateinamen, die nicht mit einem Punkt beginnen). .*stellt alle versteckten Dateien dar (dh Dateinamen, die mit einem Punkt beginnen); und *.*repräsentiert alle nicht ausgeblendeten Dateien, die einen Punkt enthalten . In echo * .*müssen die beiden Globs getrennt sein, da es sich um unterschiedliche Globs handelt: einer für nicht ausgeblendete und einer für ausgeblendete. Obwohl ich in meiner Antwort geschrieben habe, können Sie *versteckte Dateien einschließen, indem Sie die dotglobShell-Option aktivieren.
wjandrea
1
Die Verwendung von *.*ist unter Windows (DOS) üblich, um alle Dateien aufzulisten. Unter * nix werden jedoch nur Dateien mit einem Punkt in die Liste aufgenommen, was unter * nix keinen Sinn ergibt. Stattdessen verwenden Sie, *um alle Dateien außer versteckten Dateien aufzulisten und .*versteckte Dateien aufzulisten.
thomasrutter
10

Sie müssen die -d skipOption hinzufügen.

  1. Grep sucht in Dateien. Sie können, wie Sie sagten, rekursiv suchen, wenn Sie Dateien in einem Verzeichnis suchen möchten.

  2. Standardmäßig liest grep alle Dateien und erkennt die Verzeichnisse. Da Sie mit der -dOption standardmäßig nicht definiert haben, was mit den Verzeichnissen geschehen soll, wird eine Fehlerausgabe ausgegeben.

  3. Nur innerhalb des übergeordneten Verzeichnisses zu suchen, wäre `grep -d" string "überspringen ./*

anonymous2
quelle
Weitere Informationen zu grep finden Sie unter man grep.
Anonym2
(a) Bitte beachten Sie die Bearbeitung. (b) Verwenden -d skipfunktioniert nicht; es ist im Grunde das gleiche wie -s; siehe auch die Bearbeitung. (c) Nein, grep -d skip "string" ./*funktioniert auch nicht.
John Red
7

Oldtimer würden dies wahrscheinlich tun:

find . -type f -print0 | xargs -0 grep "string"
Dathompson
quelle
3
Warum nicht find . -type f -exec grep string {} +?
wchargin
5
Du willst auch -maxdepth 1.
Wchargin
@ Wchargin: Wenn Sie den Dateinamen in der Ausgabe möchten, wenn es nur eine Datei gibt, denke ich, dass Sie wollenfind . -type f -maxdepth 1 -exec grep string /dev/null {} +
Gregory Nisbet
1
@ GregoryNisbet: Übergeben Sie einfach -Hzu grep.
wchargin
2

Umformulierung - Sie möchten die Dateien in einer Ebene des Unterverzeichnisses speichern, aber nicht alle Unterverzeichnisse erneut durchsuchen?

grep forthis  *  */*

Oder wenn Sie die Dateien nicht im aktuellen Verzeichnis haben möchten

grep forthis  */*

Beachten Sie, dass Verzeichnisse, die mit einem Punkt beginnen, nicht gefunden werden.

grep forthis  .*/*    */*   

sollte diesen Job machen.

Es gibt auch -maxdepthund -mindepthEinschränkungsparameter für den findBefehl.

Criggie
quelle
Wäre es nicht möglich, grep forthis */*Dateien sowohl im aktuellen als auch in einem darunter liegenden Verzeichnis zu suchen?
Hashim
@Hashim Nein, meistens - weil es */*nur einen Schrägstrich gibt. Wenn Sie a/bim aktuellen Verzeichnis eine Datei mit dem Namen * / * hätten, würde das passen.
Criggie
0

Hier ist ein Beispiel, um Verzeichnisse zu überspringen, ohne alle Fehler zu überspringen:

grep --directories='skip' 'searchString' *
Mojtaba Rezaeian
quelle