Wenn ich find
alle PDF-Dateien im /home
Verzeichnis 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?
command-line
bash
find
Solfish
quelle
quelle
Antworten:
Was Sie versucht haben, hat nicht funktioniert, da die
access denied
Ausgabe Fehler sind und auf STDERR anstelle von STDOUT gesendet werden, an das weitergeleitet wirdgrep
.Sie können diese Fehler vermeiden, indem Sie nur STDERR umleiten
Oder wie David Foerster kommentierte , können wir STDERR prägnanter schließen
Ich vermute jedoch, dass Sie eigentlich nur Ihr Zuhause durchsuchen möchten und nicht das anderer Benutzer. Vielleicht möchten Sie es wirklich
Wenn dies zu Fehlern führt, gibt es möglicherweise falsche Eigentümer in Ihrer lokalen Konfiguration, die Sie untersuchen sollten.
quelle
sudo chown $USER: ~/.gvfs ~/.dbus
2>&-
. GNU find wird sich nicht selbst beenden, wenn versucht wird, Fehlermeldungen in einen fehlerhaften Dateideskriptor zu schreiben. Denn die Besitzproblemesudo chown -R $USER: ...
wären effektiver, wenn mehr Dateien darin nicht Eigentum von sind$USER
.Der verweigerte Zugriff wird wahrscheinlich
stderr
eher gedruckt alsstdout
.Versuche dies:
Das
2>&1
leitet die Ausgabe vonstderr
nach umstdout
, damitgrep -v
diese ihre Arbeit erledigen kann. (Standardmäßig|
nur Rohrestdout
und nichtstderr
.)quelle
2>&1
... Ich bin kein Bash-Experte. Wenn das falsch ist, sagen Sie es bitte :)bash
in Ubuntu hat es, außer im POSIX-Modus . Ich denke, es ist die beste Lösung - eine Datei mit böswilligem Namenaccess denied
wird weiterhin angezeigt.Sie meinen wahrscheinlich "Berechtigung verweigert" - was
find
in 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:
(Normalerweise möchten Sie einige Argumente übergeben
find
. Diese gehen vor der ersten Umleitung3>&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/null
oder indem sie sie mit Schließung2>&-
-Läuft das Risiko von Fehlern anderer als „Zugriff verweigert“ fehlt."Berechtigung verweigert" ist wahrscheinlich der häufigste Fehler, der beim Ausführen
find
auftritt, aber bei weitem nicht der einzig mögliche Fehler. Wenn ein anderer auftritt, möchten Sie möglicherweise etwas darüber wissen. Insbesondere wirdfind
"Keine solche Datei oder kein solches Verzeichnis" gemeldet, wenn kein Startpunkt vorhanden ist. Gibt bei mehreren Startpunktenfind
möglicherweise noch einige nützliche Ergebnisse zurück und scheint zu funktionieren. Wenn beispielsweise vorhandena
undc
vorhanden ist, diesb
jedoch nicht der Fall ist, werden diefind a b c -name x
Ergebnisse ina
"Keine solche Datei oder ein solches Verzeichnis" fürb
und dann in ausgegebenc
.Wenn Sie stdout und stderr zu stdout kombinieren und an
grep
oder einen anderen Befehl zum Filtern weiterleiten, besteht - wie bei2>&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). Einigefind
Aktionen, einschließlich der-print
Aktion, die implizit ist, wenn Sie keine Aktion angeben, bestimmen, wie Dateinamen ausgegeben werden, basierend darauf, ob stdout ein Terminal ist oder nicht .?
stattdessen gedruckt.grep
) weiterleiten, wirdfind
kein 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, diefind
das Terminal sind Erkennung soll verhindern.man find
Weitere 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
find
Implementierung 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ßlichbash
) unterstützen|&
das Piping beider Streams - oder Sie können stderr zuerst mit stdout umleiten2>&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
grep
Filtern an weiterzuleiten:Normalerweise übergeben Sie Argumente an
find
Startpunkte (die Orte, an denen gesucht werden soll, normalerweise Verzeichnisse) und Prädikate (Tests und Aktionen). Diese gehen anstelle vonargs
oben.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.
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>&2
leitet 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.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:Es ist die Gruppierung, nicht speziell die Unterschale, die diese Arbeit macht. Wenn Sie es vorziehen, können Sie verwenden
{
;}
: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
find
das stderr des Befehls zu einemgrep
Befehl umleiten,grep
der es filtert, und das stdout dieses Befehls zu stderr umleiten .Dank geht an Android Dev für diese Idee.
1>&2
. Ich habe verwendet>&2
, was äquivalent ( verwandt ) ist. Verwenden Sie, was Sie möchten.Obwohl
bash
Stützen Substitutionsprozess,sh
in Ubuntu istdash
, 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 esbash
im POSIX-Modus ausgeführt wird , ist die Unterstützung für die Prozessersetzung deaktiviert.Eine Situation, in
bash
der im POSIX-Modus ausgeführt wird, ist der Aufruf alssh
1 . Daher funktioniert unter einem Betriebssystem wie Fedora, auf dem esbash
verfügbar ist/bin/sh
, oder wenn Sie den/bin/sh
Symlinkbash
unter Ubuntu auf sich selbst hingewiesen haben, die Prozessersetzung in einemsh
Skript 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
bash
POSIX-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
tmp
Unterverzeichnis 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ösenfind
.Eines der Verzeichnisse, auf die zugegriffen werden kann, enthält eine Datei mit dem Namen "Berechtigung verweigert". Wenn Sie
find
ohne 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:Wenn Sie sowohl stdout als auch stderr an
grep
Zeilen 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: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:
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
find
das aktuelle Verzeichnis (.
) als einen Startpunkt ausgeführt, aber das nicht vorhandene Verzeichnisfoo
als einen anderen:Überprüfen
find
, ob der Standardausgang noch ein Terminal istWir 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
tmp
Verzeichnis befinden.)Erstellen Sie eine Datei mit einem Zeilenumbruch im Namen:
Normalerweise verwenden wir Verzeichnisse als Ausgangspunkte für
find
, aber Dateien funktionieren auch:Wenn Sie stdout an einen anderen Befehl weiterleiten, wird die neue Zeile buchstäblich ausgegeben, wodurch der falsche Eindruck von zwei separaten Suchergebnissen
abc
und entstehtdef
. Wir können das testen mitcat
:Das Umleiten von nur stderr verursacht dieses Problem nicht:
Es schließt auch nicht:
Rohrleitungen
grep
hat das Problem verursachen:(Das Ersetzen
|&
durch2>&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:Das Gruppieren dieses Befehls und das Zurücktauschen der Streams verursacht kein Problem:
(Die
{
;}
Version erzeugt die gleiche Ausgabe.)Die Verwendung der Prozessersetzung zum Filtern von stderr verursacht auch kein Problem:
quelle