Vous voulez probablement dire "Autorisation refusée" - qui est ce qui find
dans 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 à find
ceux-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/null
ou 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, find
signale "Aucun fichier ou répertoire" si aucun point de départ n'existe. Avec plusieurs points de départ, find
peut toujours renvoyer des résultats utiles et semble fonctionner. Par exemple, si a
et c
existe mais b
n'existe pas, find a b c -name x
affiche 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 grep
ou 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 find
actions, y compris l' -print
action 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 find
ne 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 find
le terminal la détection est destinée à empêcher.
- Voir la section NOMS DE FICHIERS INHABITUELS de
man find
pour 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' find
implé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 grep
pour 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 args
ci - 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>&2
redirige 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 find
stderr de la grep
commande vers une commande qui le filtre et rediriger grep
le 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, sh
Ubuntu 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' bash
exécution en mode POSIX , la prise en charge de la substitution de processus est désactivée.
Une situation où bash
s'exécute en mode POSIX est lorsqu'elle est appelée comme sh
1 . Par conséquent, sur un système d'exploitation comme Fedora où bash
fournit /bin/sh
, ou si vous avez fait /bin/sh
pointer le lien symbolique vers bash
vous sur Ubuntu, la substitution de processus ne fonctionne toujours pas dans un sh
script, 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, bash
active 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 tmp
sous - 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 find
sans 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 grep
et 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 find
avec le répertoire courant ( .
) comme point de départ, mais le répertoire inexistant foo
comme 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 find
la 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 tmp
ré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 abc
et 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