Suppression des lignes "Accès refusé"

9

Lorsque j'utilise findpour voir tous les fichiers pdf dans le /homerépertoire, je vois access denied. Pour les éliminer, j'ai essayé:

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

Cependant, le résultat est le même. Comment puis-je me débarrasser de ces lignes?

solfish
la source

Réponses:

19

Ce que vous avez essayé n'a pas fonctionné car les access deniedrésultats sont des erreurs et envoyés sur STDERR au lieu de STDOUT qui est canalisé grep.

Vous pouvez éviter de voir ces erreurs en redirigeant uniquement STDERR

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

Ou, comme l'a fait remarquer David Foerster, nous pouvons clôturer plus succinctement STDERR

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

Cependant, je suppose que vous ne souhaitez en fait rechercher que votre maison plutôt que celle d'autres utilisateurs, alors peut-être que vous voulez vraiment

find ~ -iname "*.pdf"

Si cela génère des erreurs, il peut y avoir des propriétés erronées dans votre configuration locale, que vous devriez étudier.

Zanna
la source
3
Grrr, pourquoi les gens me battent-ils toujours de 30 secondes? : \
You'reAGitForNotUsingGit
find: “/home/ihsan/.gvfs”: accès refusé find: “/home/ihsan/.dbus”: accès refusé, pour la commande ~
solfish
y a-t-il quelque chose de mal à cela? oui je veux aussi le répertoire personnel des autres utilisateurs qui est également créé par moi pour tester
solfish
2
@solfish Pour autant que je sache, ces fichiers devraient vous appartenir. Vous voudrez peut-êtresudo chown $USER: ~/.gvfs ~/.dbus
Zanna
1
Il devrait suffire de fermer stderr avec 2>&-. GNU find ne se terminera pas s'il essaie d'écrire des messages d'erreur dans un descripteur de fichier dysfonctionnel. Car les problèmes de propriété sudo chown -R $USER: ...seraient plus efficaces en cas de fichiers supplémentaires qui ne sont pas détenus par $USER.
David Foerster
8

L'accès refusé est probablement imprimé stderrplutôt que stdout.

Essaye ça:

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

Le 2>&1redirige la sortie de stderrvers stdout, afin que cela grep -vpuisse faire son travail. (Par défaut, |seuls les tuyaux stdoutet non stderr.)

Vous êtes AGitForNotUsingGit
la source
mais pour cela 2> & 1 signifie que si stderr existe envoyer à stdout?
solfish
@solfish Yup, c'est exactement le point :)
You'reAGitForNotUsingGit
ce que je ne comprends pas, c'est avant "|" en sortie; nous avons juste droit stderr? et après "|" en entrée, nous avons obtenu ceci
solfish
@solfish Eh bien, j'ai rencontré ce problème il y a environ un an et demi, et j'ai pu le résoudre en utilisant une méthode différente . Mais alors un commentaire ci-dessous ma réponse a suggéré d'utiliser simplement 2>&1... Je ne suis pas un expert bash, donc si c'est incorrect alors veuillez le dire :)
You'reAGitForNotUsingGit
@AndroidDev Je suggère d'ajouter cette méthode différente à cette réponse comme alternative. La critique d'Etan Reisner était que la substitution de processus n'est pas portable. Mais bashdans Ubuntu l'a, sauf en mode POSIX . Je pense que c'est la meilleure solution - un fichier malicieusement nommé access deniedapparaîtra toujours.
Eliah Kagan
4

Vous voulez probablement dire "Autorisation refusée" - qui est ce qui finddans Ubuntu vous montre lorsque vous ne pouvez pas accéder à quelque chose en raison des autorisations de fichier - plutôt que "accès refusé".

Une commande entièrement générale qui le fait correctement (et, en bonus, est portable sur d'autres * nix es, tant que le message d'erreur est le même) est:

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

(Habituellement, vous voulez transmettre des arguments à findceux-ci avant la première redirection 3>&1.)

Cependant, vous pourrez souvent utiliser quelque chose de plus simple. Par exemple, vous pouvez probablement utiliser la substitution de processus . Les détails suivent.

Les méthodes les plus courantes et leurs limites

Les deux approches typiques sont de jeter stderr (comme dans la réponse de Zanna ) ou de rediriger stderr vers stdout et de filtrer stdout (comme dans la réponse d'Android Dev ). Bien qu'elles présentent l'avantage d'être simples à écrire et constituent souvent des choix raisonnables, ces approches ne sont pas idéales.

La suppression de tout ce qui est envoyé à stderr , par exemple en le redirigeant vers le périphérique nul avec 2>/dev/nullou en le fermant avec, 2>&-entraîne le risque de manquer des erreurs autres que "Autorisation refusée".

"Autorisation refusée" est probablement l'erreur la plus courante lors de l'exécution find, mais elle est loin d'être la seule erreur possible, et si une autre se produit, vous voudrez peut-être la connaître. En particulier, findsignale "Aucun fichier ou répertoire" si aucun point de départ n'existe. Avec plusieurs points de départ, findpeut toujours renvoyer des résultats utiles et semble fonctionner. Par exemple, si aet cexiste mais bn'existe pas, find a b c -name xaffiche les résultats dans a, puis "Aucun fichier ou répertoire" pour b, puis les résultats dans c.

La combinaison de stdout et stderr ensemble dans stdout et la canalisation vers grepou une autre commande pour le filtrer, comme avec 2>&1 | grep ...ou, |& grep ...entraîne le risque de filtrer involontairement un fichier dont le nom contient le message filtré.

Par exemple, si vous filtrez les lignes qui contiennent «Autorisation refusée», vous supprimerez également les résultats de recherche affichant des noms de fichiers tels que «Autorisation refusée messages.txt». Cela se produirait probablement par accident, mais il serait également possible de donner à un fichier un nom spécialement conçu pour contrecarrer vos recherches.

Le filtrage des flux combinés présente un autre problème, qui ne peut pas être atténué par un filtrage plus sélectif (comme grep -vx 'find: .*: Permission denied'sur le côté droit du tuyau). Certaines findactions, y compris l' -printaction qui est implicite lorsque vous ne spécifiez aucune action, déterminent comment sortir les noms de fichiers selon que stdout est ou non un terminal.

  • S'il ne s'agit pas d' un terminal, les noms de fichiers sont affichés tels quels, même s'ils contiennent des caractères étranges tels que des sauts de ligne et des caractères de contrôle qui pourraient modifier le comportement de votre terminal. S'il s'agit d' un terminal, ces caractères sont supprimés et ?imprimés à la place.
  • C'est généralement ce que vous voulez. Si vous envisagez de poursuivre le traitement des noms de fichiers, ils doivent être affichés littéralement. Cependant, si vous souhaitez les afficher, un nom de fichier avec une nouvelle ligne pourrait autrement imiter plusieurs noms de fichiers, et un nom de fichier avec une séquence de caractères de retour arrière pourrait sembler être un nom différent. D'autres problèmes sont également possibles, tels que les noms de fichiers contenant des séquences d'échappement qui changent les couleurs de votre terminal.
  • Mais en canalisant les résultats de la recherche via une autre commande (comme grep), vous findne voyez plus de terminal. (Plus précisément, cela empêche sa sortie standard d'être un terminal.) Ensuite, des caractères étranges sont littéralement affichés. Mais si toute la commande sur le côté droit du tuyau consiste à (a) supprimer les lignes qui ressemblent à des messages "Autorisation refusée" et (b) imprimer ce qui reste, alors vous êtes toujours soumis au genre de manigances qui sont findle terminal la détection est destinée à empêcher.
  • Voir la section NOMS DE FICHIERS INHABITUELS de man findpour plus d'informations, y compris le comportement de chacune des actions qui impriment les noms de fichiers. ( "De nombreuses actions de find entraînent l'impression de données qui sont sous le contrôle d'autres utilisateurs ..." ) Voir aussi les sections 3.3.2.1 , 3.3.2.2 et 3.3.2.3 du manuel de référence GNU Findutils .

La discussion ci-dessus des noms de fichiers inhabituels concerne GNU find , qui est l' findimplémentation dans les systèmes GNU / Linux, y compris Ubuntu.

Laisser la sortie standard seule pendant le filtrage de l'erreur standard

Ce que vous vraiment voulez ici est de laisser stdout intact tandis que la tuyauterie stderr à grep. Malheureusement, il n'y a pas de syntaxe simple pour cela. |tuyaux stdout, et certains shells (y compris bash) prennent |&en charge le tuyau des deux flux - ou vous pouvez d'abord rediriger stderr vers stdout 2>&1 |, ce qui a le même effet. Mais les shells couramment utilisés ne fournissent pas de syntaxe pour canaliser stderr uniquement.

Vous pouvez toujours le faire. C'est juste gênant. Une façon consiste à échanger stdout avec stderr , afin que les résultats de la recherche soient sur stderr et que les erreurs soient sur stdout, puis dirigez stdout vers greppour filtrer:

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

Habituellement, vous passerez des arguments à find, tels que des points de départ (les endroits à rechercher, qui sont généralement des répertoires) et des prédicats (tests et actions). Ceux-ci vont à la place de argsci - dessus.

Cela fonctionne en introduisant un nouveau descripteur de fichier pour conserver l'un des deux flux standard que vous souhaitez échanger, en effectuant des redirections pour les échanger et en fermant le nouveau descripteur de fichier.

  • Le descripteur de fichier 1 est stdout et 2 est stderr (et le 0 non redirigé est stdin ). Mais vous pouvez également rediriger en utilisant d'autres descripteurs de fichiers. Cela peut être utilisé pour ouvrir ou garder ouvert un fichier ou un périphérique.
  • 3>&1 redirige le descripteur de fichier 3 vers stdout, de sorte que lorsque stdout (descripteur de fichier 1) est ensuite redirigé, la stdout d'origine peut toujours être écrite facilement.
  • 1>&2redirige stdout vers stderr. Étant donné que le descripteur de fichier 3 est toujours la sortie standard d'origine, il est toujours accessible.
  • 2>&3 redirige stderr vers le descripteur de fichier 3, qui est la sortie standard d'origine.
  • 3>&- ferme le descripteur de fichier 3, qui n'est plus nécessaire.
  • Pour plus d'informations, voir Comment diriger stderr et non stdout? et IO Redirection - Échange stdout et stderr (Advanced) et en particulier ne canalisez que stderr à travers un filtre .

Cependant, cette méthode présente l'inconvénient que les résultats de la recherche sont envoyés à stderr et que les erreurs sont envoyées à stdout . Si vous exécutez cette commande directement dans un shell interactif et que vous ne redirigez pas ou ne redirigez plus la sortie, cela n'a pas vraiment d'importance. Sinon, cela pourrait être un problème. Si vous placez cette commande dans un script, puis que quelqu'un (peut-être vous, plus tard) redirige ou redirige sa sortie, elle ne se comporte pas comme prévu .

La solution consiste à échanger les flux une fois que vous avez terminé de filtrer la sortie . L'application des mêmes redirections indiquées ci-dessus sur le côté droit du pipeline ne permettra pas d'atteindre cet objectif, car |seuls les canaux stdout, de sorte que ce côté du pipeline ne reçoit que la sortie qui a été initialement envoyée à stderr (car les flux ont été échangés) et non l'original sortie standard. Au lieu de cela, vous pouvez utiliser ( )pour exécuter la commande ci-dessus dans un sous-shell ( lié ), puis appliquer les redirections d'échange à cela:

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

C'est le regroupement, pas spécifiquement le sous-shell, qui fait que cela fonctionne. Si vous préférez, vous pouvez utiliser { ;}:

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

Une méthode moins lourde: la substitution de processus

Certains shells, y compris Bash sur les systèmes qui peuvent le prendre en charge (y compris les systèmes GNU / Linux comme Ubuntu), vous permettent d'effectuer une substitution de processus , ce qui vous permet d'exécuter une commande et de rediriger vers / depuis l'un de ses flux. Vous pouvez rediriger le findstderr de la grepcommande vers une commande qui le filtre et rediriger greple stdout de cette commande vers stderr.

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

Nous remercions Android Dev pour cette idée.

Bien qu'il bash supporte la substitution de processus, shUbuntu l' est dash, ce qui n'est pas le cas. Il vous donnera "Erreur de syntaxe: redirection inattendue" si vous essayez d'utiliser cette méthode, tandis que la méthode de permutation de stdout et stderr fonctionnera toujours. En outre, lors de l' bashexécution en mode POSIX , la prise en charge de la substitution de processus est désactivée.

Une situation où bashs'exécute en mode POSIX est lorsqu'elle est appelée comme sh1 . Par conséquent, sur un système d'exploitation comme Fedora où bashfournit /bin/sh, ou si vous avez fait /bin/shpointer le lien symbolique vers bashvous sur Ubuntu, la substitution de processus ne fonctionne toujours pas dans un shscript, sans commande préalable pour désactiver le mode POSIX. Votre meilleur pari, si vous souhaitez utiliser cette méthode dans un script, est de mettre #!/bin/bash en haut au lieu de #!/bin/sh, si vous ne l'êtes pas déjà.

1 : Dans cette situation, bashactive automatiquement le mode POSIX après avoir exécuté les commandes dans ses scripts de démarrage.

Un exemple

Il est utile de pouvoir tester ces commandes. Pour ce faire, je crée un tmpsous - répertoire du répertoire en cours et je le remplis avec certains fichiers et répertoires, en retirant des autorisations à l'un d'eux pour déclencher une erreur "Autorisation refusée" dans 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

L' un des répertoires qui est accessible comprend un fichier avec « Autorisation refusée » dans son nom. L'exécution findsans redirections ni canaux affiche ce fichier, mais affiche également l'erreur réelle "Autorisation refusée" pour un autre répertoire qui n'est pas accessible:

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

Le fait de canaliser à la fois stdout et stderr vers grepet filtrer les lignes qui contiennent "Autorisation refusée" fait disparaître le message d'erreur mais masque également le résultat de la recherche pour le fichier avec cette phrase dans son nom:

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

find 2>&1 | grep -Fv 'Permission denied' est équivalent et produit la même sortie.

Les méthodes indiquées ci-dessus pour filtrer "Autorisation refusée" uniquement à partir des messages d'erreur - et non des résultats de la recherche - ont réussi. Par exemple, voici la méthode où stdout et stderr sont échangés:

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) produit la même sortie.

Vous pouvez déclencher un message d'erreur différent pour vous assurer que les lignes envoyées à stderr qui ne contiennent pas le texte "Autorisation refusée" sont toujours autorisées. Par exemple, ici j'ai couru findavec le répertoire courant ( .) comme point de départ, mais le répertoire inexistant foocomme autre:

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

Vérifier que findla sortie standard est toujours un terminal

Nous pouvons également voir quelles commandes provoquent l'affichage littéral des caractères spéciaux, tels que les retours à la ligne. (Cela peut être fait séparément de la démonstration ci-dessus, et il n'est pas nécessaire qu'il se trouve dans le tmprépertoire.)

Créez un fichier avec une nouvelle ligne dans son nom:

touch $'abc\ndef'

Habituellement, nous utilisons des répertoires comme points de départ find, mais les fichiers fonctionnent aussi:

$ find abc*
abc?def

L'ajout de stdout à une autre commande entraîne la sortie littérale de la nouvelle ligne, créant la fausse impression de deux résultats de recherche distincts abcet de def. Nous pouvons le tester avec cat:

$ find abc* | cat
abc
def

Rediriger juste stderr ne cause pas ce problème:

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

La fermeture non plus:

$ find abc* 2>&-
abc?def

La tuyauterie à grep fait la cause du problème:

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

(Le remplacement |&par 2>&1 |est équivalent et produit la même sortie.)

L'échange de stdout et stderr et de la tuyauterie stdout ne cause pas le problème find- la stdout de devient stderr, qui n'est pas canalisée:

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

Le regroupement de cette commande et l'échange des flux en arrière ne provoque pas le problème:

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

(La { ;}version produit la même sortie.)

L'utilisation de la substitution de processus pour filtrer stderr ne cause pas non plus le problème:

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