Herausnehmen 'Zugriff verweigert "Zeilen

9

Wenn ich findalle PDF-Dateien im /homeVerzeichnis sehe, sehe ich access denied. Um sie zu beseitigen, habe ich versucht:

find /home -iname "*.pdf" | grep -v "access denied"

Das Ergebnis ist jedoch das gleiche. Wie kann ich diese Zeilen loswerden?

Solfish
quelle

Antworten:

19

Was Sie versucht haben, hat nicht funktioniert, da die access deniedAusgabe Fehler sind und auf STDERR anstelle von STDOUT gesendet werden, an das weitergeleitet wird grep.

Sie können diese Fehler vermeiden, indem Sie nur STDERR umleiten

find /home -iname "*.pdf" 2>/dev/null

Oder wie David Foerster kommentierte , können wir STDERR prägnanter schließen

find /home -iname "*.pdf" 2>&-

Ich vermute jedoch, dass Sie eigentlich nur Ihr Zuhause durchsuchen möchten und nicht das anderer Benutzer. Vielleicht möchten Sie es wirklich

find ~ -iname "*.pdf"

Wenn dies zu Fehlern führt, gibt es möglicherweise falsche Eigentümer in Ihrer lokalen Konfiguration, die Sie untersuchen sollten.

Zanna
quelle
3
Grrr, warum schlagen mich die Leute immer um 30 Sekunden? : \
You'reAGitForNotUsingGit
find: "/home/ihsan/.gvfs": Zugriff verweigert find: "/home/ihsan/.dbus": Zugriff verweigert, für den Befehl ~
solfish
Stimmt etwas nicht? Ja, ich möchte auch zu anderen Benutzern Home-Verzeichnis, das auch von mir erstellt wurde, um zu testen
Solfish
2
@solfish Soweit ich weiß, sollten diese Dateien Ihnen gehören. Vielleicht möchten Siesudo chown $USER: ~/.gvfs ~/.dbus
Zanna
1
Es sollte ausreichen, um stderr mit zu schließen 2>&-. GNU find wird sich nicht selbst beenden, wenn versucht wird, Fehlermeldungen in einen fehlerhaften Dateideskriptor zu schreiben. Denn die Besitzprobleme sudo chown -R $USER: ...wären effektiver, wenn mehr Dateien darin nicht Eigentum von sind $USER.
David Foerster
8

Der verweigerte Zugriff wird wahrscheinlich stderreher gedruckt als stdout.

Versuche dies:

find /home -iname "*.pdf" 2>&1 | grep -v "access denied"

Das 2>&1leitet die Ausgabe von stderrnach um stdout, damit grep -vdiese ihre Arbeit erledigen kann. (Standardmäßig |nur Rohre stdoutund nicht stderr.)

Du bistAGitForNotUsingGit
quelle
aber dafür bedeutet 2> & 1, wenn stderr existiert, an stdout senden?
Solfish
@solfish Yup, das ist genau der Punkt :)
You'reAGitForNotUsingGit
was ich nicht verstehe ist vor "|" als Ausgabe; wir haben gerade stderr richtig? und nach "|" Als Eingabe haben wir dies
Solfish
@solfish Nun, ich bin vor ungefähr anderthalb Jahren auf dieses Problem gestoßen und konnte es mit einer anderen Methode beheben . Aber dann schlug ein Kommentar unter meiner Antwort vor, einfach zu verwenden 2>&1... Ich bin kein Bash-Experte. Wenn das falsch ist, sagen Sie es bitte :)
You'reAGitForNotUsingGit
@AndroidDev Ich schlage vor, dieser Antwort alternativ diese andere Methode hinzuzufügen. Etan Reisners Kritik war, dass die Prozessersetzung nicht portabel ist. Aber bashin Ubuntu hat es, außer im POSIX-Modus . Ich denke, es ist die beste Lösung - eine Datei mit böswilligem Namen access deniedwird weiterhin angezeigt.
Eliah Kagan
4

Sie meinen wahrscheinlich "Berechtigung verweigert" - was findin Ubuntu angezeigt wird , wenn Sie aufgrund von Dateiberechtigungen nicht auf etwas zugreifen können - und nicht "Zugriff verweigert".

Ein ganz allgemeiner Befehl, der dies korrekt ausführt (und als Bonus auf andere * nixes portierbar ist , solange die Fehlermeldung dieselbe ist), lautet:

(find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-

(Normalerweise möchten Sie einige Argumente übergeben find. Diese gehen vor der ersten Umleitung 3>&1.)

Oft können Sie jedoch etwas Einfacheres verwenden. Beispielsweise können Sie wahrscheinlich die Prozessersetzung verwenden . Details folgen.

Die gebräuchlichsten Methoden und ihre Grenzen

Die beiden typischen Ansätze bestehen darin, stderr wegzuwerfen (wie in Zannas Antwort ) oder stderr nach stdout umzuleiten und stdout zu filtern (wie in der Antwort von Android Dev ). Obwohl sie den Vorteil haben, einfach zu schreiben zu sein und oft eine vernünftige Wahl sind, sind diese Ansätze nicht ideal.

Wegwerfen alles geschickt Stderr -such wie es um die Umleitung Null - Gerät mit 2>/dev/nulloder indem sie sie mit Schließung 2>&--Läuft das Risiko von Fehlern anderer als „Zugriff verweigert“ fehlt.

"Berechtigung verweigert" ist wahrscheinlich der häufigste Fehler, der beim Ausführen findauftritt, aber bei weitem nicht der einzig mögliche Fehler. Wenn ein anderer auftritt, möchten Sie möglicherweise etwas darüber wissen. Insbesondere wird find"Keine solche Datei oder kein solches Verzeichnis" gemeldet, wenn kein Startpunkt vorhanden ist. Gibt bei mehreren Startpunkten findmöglicherweise noch einige nützliche Ergebnisse zurück und scheint zu funktionieren. Wenn beispielsweise vorhanden aund cvorhanden ist, dies bjedoch nicht der Fall ist, werden die find a b c -name xErgebnisse in a"Keine solche Datei oder ein solches Verzeichnis" für bund dann in ausgegeben c.

Wenn Sie stdout und stderr zu stdout kombinieren und an grepoder einen anderen Befehl zum Filtern weiterleiten, besteht - wie bei 2>&1 | grep ...oder |& grep ...- die Gefahr, dass eine Datei, deren Name die zu filternde Nachricht enthält, unbeabsichtigt herausgefiltert wird.

Wenn Sie beispielsweise Zeilen herausfiltern, die "Berechtigung verweigert" enthalten, werden auch Suchergebnisse gelöscht, die Dateinamen wie "Berechtigung verweigert messages.txt" anzeigen. Dies würde wahrscheinlich zufällig passieren, obwohl es auch möglich wäre, einer Datei einen speziell gestalteten Namen zu geben, um Ihre Suche zu vereiteln.

Das Filtern der kombinierten Streams weist ein weiteres Problem auf, das nicht durch selektiveres Filtern gemindert werden kann (z. B. grep -vx 'find: .*: Permission denied'auf der rechten Seite des Rohrs). Einige findAktionen, einschließlich der -printAktion, die implizit ist, wenn Sie keine Aktion angeben, bestimmen, wie Dateinamen ausgegeben werden, basierend darauf, ob stdout ein Terminal ist oder nicht .

  • Wenn es sich nicht um ein Terminal handelt, werden die Dateinamen unverändert ausgegeben, auch wenn sie seltsame Zeichen wie Zeilenumbrüche und Steuerzeichen enthalten, die das Verhalten Ihres Terminals ändern können. Wenn es sich um ein Terminal handelt, werden diese Zeichen unterdrückt und ?stattdessen gedruckt.
  • Dies ist normalerweise das, was Sie wollen. Wenn Sie Dateinamen weiter verarbeiten möchten, müssen sie buchstäblich ausgegeben werden. Wenn Sie sie jedoch anzeigen möchten, könnte ein Dateiname mit einer neuen Zeile andernfalls mehrere Dateinamen imitieren, und ein Dateiname mit einer Folge von Rücktastezeichen könnte ein anderer Name sein. Andere Probleme sind ebenfalls möglich, z. B. Dateinamen mit Escape-Sequenzen, die die Farben in Ihrem Terminal ändern.
  • Wenn Sie die Suchergebnisse jedoch über einen anderen Befehl (wie grep) weiterleiten, wird findkein Terminal mehr angezeigt. (Genauer gesagt, es bewirkt, dass sein Standard nicht ein Terminal ist.) Dann werden seltsame Zeichen buchstäblich ausgegeben. Wenn der Befehl auf der rechten Seite der Pipe jedoch nur lautet: (a) Entfernen von Zeilen, die wie "Berechtigung verweigert" -Nachrichten aussehen, und (b) Drucken der verbleibenden Elemente, sind Sie immer noch der Art von Spielereien ausgesetzt, die finddas Terminal sind Erkennung soll verhindern.
  • man findWeitere Informationen, einschließlich des Verhaltens der einzelnen Aktionen, die Dateinamen drucken, finden Sie im Abschnitt UNGEWÖHNLICHE DATEIEN von . ( "Viele der Suchaktionen führen zum Drucken von Daten, die von anderen Benutzern gesteuert werden ..." ) Siehe auch Abschnitte 3.3.2.1 , 3.3.2.2 und 3.3.2.3 des GNU Findutils-Referenzhandbuchs .

Die obige Diskussion ungewöhnlicher Dateinamen bezieht sich auf GNU find , die findImplementierung in GNU / Linux-Systemen einschließlich Ubuntu.

Lassen Sie die Standardausgabe allein, während Sie den Standardfehler filtern

Was Sie wirklich hier wollen , ist zu verlassen stdout intakt , während kochend stderr zu grep. Leider gibt es dafür keine einfache Syntax. |pipes stdout und einige Shells (einschließlich bash) unterstützen |&das Piping beider Streams - oder Sie können stderr zuerst mit stdout umleiten 2>&1 |, was den gleichen Effekt hat. Die häufig verwendeten Shells bieten jedoch keine Syntax nur für Pipe-Stderr.

Sie können dies immer noch tun. Es ist nur umständlich. Eine Möglichkeit besteht darin, stdout mit stderr zu tauschen , sodass die Suchergebnisse auf stderr und Fehler auf stdout sind, und dann stdout zum grepFiltern an weiterzuleiten:

find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'

Normalerweise übergeben Sie Argumente an findStartpunkte (die Orte, an denen gesucht werden soll, normalerweise Verzeichnisse) und Prädikate (Tests und Aktionen). Diese gehen anstelle von argsoben.

Dies funktioniert, indem Sie einen neuen Dateideskriptor einführen , um einen der beiden Standard-Streams beizubehalten, die Sie austauschen möchten, Umleitungen ausführen, um sie auszutauschen, und den neuen Dateideskriptor schließen.

  • Der Dateideskriptor 1 ist stdout und 2 ist stderr (und die nicht umgeleitete 0 ist stdin ). Sie können aber auch mit anderen Dateideskriptoren umleiten. Dies kann verwendet werden, um eine Datei oder ein Gerät zu öffnen oder offen zu halten.
  • 3>&1 Leitet den Dateideskriptor 3 nach stdout um, sodass beim anschließenden Umleiten von stdout (Dateideskriptor 1) das ursprüngliche stdout weiterhin problemlos beschrieben werden kann.
  • 1>&2leitet stdout an stderr weiter. Da der Dateideskriptor 3 immer noch der ursprüngliche Standard ist, kann weiterhin auf ihn zugegriffen werden.
  • 2>&3 leitet stderr an den Dateideskriptor 3 weiter, der das ursprüngliche stdout ist.
  • 3>&- Schließt den Dateideskriptor 3, der nicht mehr benötigt wird.
  • Weitere Informationen finden Sie unter So leiten Sie stderr und nicht stdout weiter. und E / A-Umleitung - Vertauschen von stdout und stderr (Erweitert) und insbesondere von Pipe only stderr durch einen Filter .

Diese Methode hat jedoch den Nachteil, dass Suchergebnisse an stderr und Fehler an stdout gesendet werden . Wenn Sie diesen Befehl direkt in einer interaktiven Shell ausführen und die Ausgabe nicht weiterleiten oder umleiten, spielt dies keine Rolle. Andernfalls kann es ein Problem sein. Wenn Sie diesen Befehl in ein Skript einfügen und dann jemand (vielleicht Sie später) seine Ausgabe umleitet oder weiterleitet, verhält er sich nicht wie erwartet .

Die Lösung besteht darin, die Streams zurückzutauschen, nachdem Sie die Ausgabe gefiltert haben . Wenn Sie dieselben Umleitungen anwenden, die oben auf der rechten Seite der Pipeline gezeigt wurden, wird dies nicht erreicht, da |nur Pipes stdout ausgeführt werden, sodass diese Seite der Pipeline nur Ausgaben empfängt, die ursprünglich an stderr gesendet wurden (weil die Streams ausgetauscht wurden) und nicht das Original Standardausgabe. Stattdessen können Sie ( )den obigen Befehl in einer Subshell ( verwandt ) ausführen und dann die Swap-Umleitungen auf Folgendes anwenden:

(find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-

Es ist die Gruppierung, nicht speziell die Unterschale, die diese Arbeit macht. Wenn Sie es vorziehen, können Sie verwenden { ;}:

{ find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'; } 3>&1 1>&2 2>&3 3>&-

Weniger umständlich: Prozesssubstitution

Mit einigen Shells, einschließlich Bash auf Systemen, die dies unterstützen (einschließlich GNU / Linux-Systemen wie Ubuntu), können Sie eine Prozessersetzung durchführen , mit der Sie einen Befehl ausführen und zu / von einem seiner Streams umleiten können. Sie können finddas stderr des Befehls zu einem grepBefehl umleiten, grepder es filtert, und das stdout dieses Befehls zu stderr umleiten .

find args 2> >(grep -Fv 'Permission denied' >&2)

Dank geht an Android Dev für diese Idee.

Obwohl bash Stützen Substitutionsprozess, shin Ubuntu ist dash, das dies nicht tut. Wenn Sie versuchen, diese Methode zu verwenden, wird "Syntaxfehler: Umleitung unerwartet" angezeigt, während die Methode zum Austauschen von stdout und stderr weiterhin funktioniert. Wenn es bashim POSIX-Modus ausgeführt wird , ist die Unterstützung für die Prozessersetzung deaktiviert.

Eine Situation, in bashder im POSIX-Modus ausgeführt wird, ist der Aufruf als sh1 . Daher funktioniert unter einem Betriebssystem wie Fedora, auf dem es bashverfügbar ist /bin/sh, oder wenn Sie den /bin/shSymlink bashunter Ubuntu auf sich selbst hingewiesen haben, die Prozessersetzung in einem shSkript immer noch nicht , ohne dass zuvor der Befehl zum Deaktivieren des POSIX-Modus erteilt wurde. Ihre beste Wette, wenn Sie diese Methode in einem Skript verwendet werden sollen, ist setzen #!/bin/bash an der Spitze statt #!/bin/sh, wenn Sie nicht bereits sind.

1 : In dieser Situation wird der bashPOSIX-Modus automatisch aktiviert, nachdem die Befehle in den Startskripten ausgeführt wurden.

Ein Beispiel

Es ist nützlich, diese Befehle testen zu können. Zu diesem Zweck erstelle ich ein tmpUnterverzeichnis des aktuellen Verzeichnisses und fülle es mit einigen Dateien und Verzeichnissen, wobei einem die Berechtigungen entzogen werden, um den Fehler "Berechtigung verweigert" in auszulösen find.

mkdir tmp; cd tmp; mkdir a b c; touch w a/x 'a/Permission denied messages.txt' b/y c/z; chmod 0 b

Eines der Verzeichnisse, auf die zugegriffen werden kann, enthält eine Datei mit dem Namen "Berechtigung verweigert". Wenn Sie findohne Umleitungen oder Pipes ausführen, wird diese Datei angezeigt, aber auch der tatsächliche Fehler "Berechtigung verweigert" für ein anderes Verzeichnis, auf das nicht zugegriffen werden kann:

ek@Io:~/tmp$ find
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find: ‘./b’: Permission denied

Wenn Sie sowohl stdout als auch stderr an grepZeilen weiterleiten und diese herausfiltern, die "Berechtigung verweigert" enthalten, wird die Fehlermeldung ausgeblendet, das Suchergebnis für die Datei mit dem folgenden Ausdruck wird jedoch ausgeblendet:

ek@Io:~/tmp$ find |& grep -Fv 'Permission denied'
.
./a
./a/x
./c
./c/z
./w
./b

find 2>&1 | grep -Fv 'Permission denied' ist äquivalent und erzeugt die gleiche Ausgabe.

Die oben gezeigten Methoden zum Herausfiltern von "Berechtigung verweigert" nur aus Fehlermeldungen - und nicht aus Suchergebnissen - sind erfolgreich. Hier ist zum Beispiel die Methode, bei der stdout und stderr ausgetauscht werden:

ek@Io:~/tmp$ (find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b

find args 2> >(grep -Fv 'Permission denied' >&2) erzeugt die gleiche Ausgabe.

Sie können eine andere Fehlermeldung auslösen, um sicherzustellen, dass an stderr gesendete Zeilen, die nicht den Text "Berechtigung verweigert" enthalten, weiterhin zugelassen sind. Zum Beispiel habe ich hier finddas aktuelle Verzeichnis ( .) als einen Startpunkt ausgeführt, aber das nicht vorhandene Verzeichnis fooals einen anderen:

ek@Io:~/tmp$ (find . foo 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find: foo’: No such file or directory

Überprüfen find, ob der Standardausgang noch ein Terminal ist

Wir können auch sehen, welche Befehle dazu führen, dass Sonderzeichen wie Zeilenumbrüche buchstäblich angezeigt werden. (Dies kann getrennt von der obigen Demonstration erfolgen und muss sich nicht im tmpVerzeichnis befinden.)

Erstellen Sie eine Datei mit einem Zeilenumbruch im Namen:

touch $'abc\ndef'

Normalerweise verwenden wir Verzeichnisse als Ausgangspunkte für find, aber Dateien funktionieren auch:

$ find abc*
abc?def

Wenn Sie stdout an einen anderen Befehl weiterleiten, wird die neue Zeile buchstäblich ausgegeben, wodurch der falsche Eindruck von zwei separaten Suchergebnissen abcund entsteht def. Wir können das testen mit cat:

$ find abc* | cat
abc
def

Das Umleiten von nur stderr verursacht dieses Problem nicht:

$ find abc* 2>/dev/null
abc?def

Es schließt auch nicht:

$ find abc* 2>&-
abc?def

Rohrleitungen grep hat das Problem verursachen:

$ find abc* |& grep -Fv 'Permission denied'
abc
def

(Das Ersetzen |&durch 2>&1 |ist äquivalent und erzeugt die gleiche Ausgabe.)

Das Vertauschen von stdout und stderr und piping stdout verursacht kein Problem find- stdout wird zu stderr, das nicht geleitet wird:

$ find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
abc?def

Das Gruppieren dieses Befehls und das Zurücktauschen der Streams verursacht kein Problem:

$ (find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
abc?def

(Die { ;}Version erzeugt die gleiche Ausgabe.)

Die Verwendung der Prozessersetzung zum Filtern von stderr verursacht auch kein Problem:

$ find abc* 2> >(grep -Fv 'Permission denied' >&2)
abc?def
Eliah Kagan
quelle