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:
(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/null
oder 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 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 wird find
"Keine solche Datei oder kein solches Verzeichnis" gemeldet, wenn kein Startpunkt vorhanden ist. Gibt bei mehreren Startpunkten find
möglicherweise noch einige nützliche Ergebnisse zurück und scheint zu funktionieren. Wenn beispielsweise vorhanden a
und c
vorhanden ist, dies b
jedoch nicht der Fall ist, werden die find a b c -name x
Ergebnisse in a
"Keine solche Datei oder ein solches Verzeichnis" für b
und dann in ausgegeben c
.
Wenn Sie stdout und stderr zu stdout kombinieren und an grep
oder 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 find
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 .
- 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 find
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, die find
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ß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 grep
Filtern an weiterzuleiten:
find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
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 von args
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.
- 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>&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.
- 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 find
das stderr des Befehls zu einem grep
Befehl umleiten, grep
der 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, sh
in 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 bash
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 als sh
1 . Daher funktioniert unter einem Betriebssystem wie Fedora, auf dem es bash
verfügbar ist /bin/sh
, oder wenn Sie den /bin/sh
Symlink bash
unter Ubuntu auf sich selbst hingewiesen haben, die Prozessersetzung in einem sh
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ö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 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:
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 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:
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 find
das aktuelle Verzeichnis ( .
) als einen Startpunkt ausgeführt, aber das nicht vorhandene Verzeichnis foo
als 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 tmp
Verzeichnis 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 abc
und 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