Probablemente quiere decir "Permiso denegado", que es lo que find
en Ubuntu le muestra cuando no puede acceder a algo debido a los permisos de archivo, en lugar de "acceso denegado".
Un comando totalmente general que hace esto correctamente (y, como beneficio adicional, es portátil a otros * nix es, siempre que el mensaje de error sea el mismo) es:
(find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
(Por lo general, desea pasar algunos argumentos a find
. Esos van antes de la primera redirección 3>&1
).
Sin embargo, a menudo podrá usar algo más simple. Por ejemplo, probablemente pueda usar la sustitución de procesos . Los detalles siguen.
Los métodos más comunes y sus limitaciones
Los dos enfoques típicos son tirar stderr (como en la respuesta de Zanna ) o redirigir stderr a stdout y filtrar stdout (como en la respuesta de Android Dev ). Aunque tienen la ventaja de ser simples de escribir y, a menudo, son opciones razonables, estos enfoques no son ideales.
Desechando todo lo envía a stderr -como por volver a enviarlos al dispositivo nulo con 2>/dev/null
o cerrando con 2>&-
-runs el riesgo de errores que no sean "Permiso denegado" faltante.
"Permiso denegado" es probablemente el error más común visto cuando se ejecuta find
, pero está lejos de ser el único error posible, y si ocurre otro, es posible que desee saber al respecto. En particular, find
informa "No existe tal archivo o directorio" si no existe un punto de partida. Con múltiples puntos de partida, find
aún puede devolver algunos resultados útiles y parece funcionar. Por ejemplo, si existe a
y no c
existe b
, find a b c -name x
imprime los resultados a
, luego "No existe tal archivo o directorio" b
y luego los resultados c
.
La combinación de stdout y stderr juntos en stdout y canalizarlo a grep
algún otro comando para filtrarlo, como con 2>&1 | grep ...
o, |& grep ...
elimina el riesgo de filtrar involuntariamente un archivo cuyo nombre contiene el mensaje que se está filtrando.
Por ejemplo, si filtra las líneas que contienen "Permiso denegado", también eliminará los resultados de búsqueda que muestren nombres de archivo como "Permiso denegado messages.txt". Esto probablemente sucedería por accidente, aunque también sería posible que un archivo reciba un nombre especialmente diseñado para frustrar sus búsquedas.
Filtrar las secuencias combinadas tiene otro problema, que no puede mitigarse filtrándolo de manera más selectiva (como grep -vx 'find: .*: Permission denied'
en el lado derecho de la tubería). Algunas find
acciones, incluida la -print
acción que está implícita cuando no especifica ninguna acción, determinan cómo generar nombres de archivos en función de si stdout es o no un terminal.
- Si no se trata de una terminal, los nombres de los archivos se muestran tal cual, incluso si contienen caracteres extraños como líneas nuevas y caracteres de control que podrían cambiar el comportamiento de su terminal. Si es un terminal, estos caracteres se suprimen y
?
se imprimen en su lugar.
- Esto suele ser lo que quieres. Si va a procesar más nombres de archivos, se deben generar literalmente. Sin embargo, si va a mostrarlos, un nombre de archivo con una nueva línea podría simular varios nombres de archivo, y un nombre de archivo con una secuencia de caracteres de retroceso podría parecer un nombre diferente. También son posibles otros problemas, como nombres de archivos que contienen secuencias de escape que cambian los colores en su terminal.
- Pero canalizar los resultados de búsqueda a través de otro comando (como
grep
) hace que find
ya no se vea una terminal. (Más precisamente, hace que su stdout no sea una terminal). Luego, los caracteres extraños se emiten literalmente. Pero si todo el comando en el lado derecho de la tubería es (a) eliminar líneas que parecen mensajes de "Permiso denegado" e (b) imprimir lo que queda, entonces todavía está sujeto a la clase de travesuras que find
es terminal La detección está destinada a prevenir.
- Consulte la sección INUSUAL FILENAMES de
man find
para obtener más información, incluido el comportamiento de cada una de las acciones que imprimen nombres de archivo. ( "Muchas de las acciones de find dan como resultado la impresión de datos que está bajo el control de otros usuarios ..." ) Consulte también las secciones 3.3.2.1 , 3.3.2.2 y 3.3.2.3 del manual de referencia de GNU Findutils .
La discusión anterior sobre nombres de archivos inusuales se refiere a GNU find , que es la find
implementación en sistemas GNU / Linux, incluido Ubuntu.
Dejando la salida estándar sola mientras se filtra el error estándar
Lo que realmente quiere aquí es dejar la salida estándar intacta, mientras que las tuberías stderr a grep
. Desafortunadamente no hay una sintaxis simple para esto. |
canaliza stdout, y algunos shells (incluidos bash
) admiten la |&
canalización de ambas secuencias, o puede redirigir stderr a stdout primero con 2>&1 |
, lo que tiene el mismo efecto. Pero los shells de uso común no proporcionan una sintaxis solo para stderr de tubería.
Aún puedes hacer esto. Es simplemente incómodo. Una forma es intercambiar stdout con stderr , de modo que los resultados de búsqueda estén en stderr y los errores estén en stdout, luego canalice stdout a grep
para filtrar:
find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
Por lo general, pasará argumentos a find
, como puntos de partida (los lugares desde donde buscar, que generalmente son directorios) y predicados (pruebas y acciones). Estos van en lugar de args
arriba.
Esto funciona mediante la introducción de un nuevo descriptor de archivo para mantener una de las dos transmisiones estándar que desea intercambiar, realizar redirecciones para intercambiarlas y cerrar el nuevo descriptor de archivo.
- El descriptor de archivo 1 es stdout y 2 es stderr (y el 0 no redirigido es stdin ). Pero también puede redirigir utilizando otros descriptores de archivo. Esto se puede usar para abrir o mantener abierto un archivo o dispositivo.
3>&1
redirige el descriptor de archivo 3 a stdout, de modo que cuando stdout (descriptor de archivo 1) se redirige posteriormente, el stdout original todavía se puede escribir fácilmente.
1>&2
redirige stdout a stderr. Dado que el descriptor de archivo 3 sigue siendo el stdout original, aún se puede acceder a él.
2>&3
redirige stderr al descriptor de archivo 3, que es el stdout original.
3>&-
cierra el descriptor de archivo 3, que ya no es necesario.
- Para obtener más información, consulte ¿Cómo canalizar stderr y no stdout? y redirección de E / S: intercambio de stdout y stderr (avanzado) y, especialmente, de tuberías solo stderr a través de un filtro .
Sin embargo, este método tiene la desventaja de que los resultados de búsqueda se envían a stderr y los errores se envían a stdout . Si está ejecutando este comando directamente en un shell interactivo y no está canalizando o redirigiendo la salida, entonces eso realmente no importa. De lo contrario, puede ser un problema. Si coloca ese comando en un script, y luego alguien (quizás usted, más adelante) redirige o canaliza su salida, no se comporta como se esperaba .
La solución es volver a intercambiar las secuencias una vez que haya terminado de filtrar la salida . La aplicación de las mismas redirecciones que se muestran arriba en el lado derecho de la tubería no logrará esto, porque |
solo las tuberías stdout, por lo que ese lado de la tubería solo recibe la salida que se envió originalmente a stderr (porque las secuencias se intercambiaron) y no el original salida estándar. En su lugar, puede usar (
)
para ejecutar el comando anterior en una subshell ( relacionado ), luego aplicar las redirecciones de intercambio a eso:
(find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
Es la agrupación, no específicamente la subshell, lo que hace que esto funcione. Si lo prefiere, puede usar {
;}
:
{ find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'; } 3>&1 1>&2 2>&3 3>&-
Una forma menos engorrosa: sustitución de procesos
Algunos shells, incluido Bash en sistemas que pueden admitirlo (incluidos los sistemas GNU / Linux como Ubuntu), le permiten realizar la sustitución del proceso , lo que le permite ejecutar un comando y redirigir a / desde uno de sus flujos. Puede redirigir el find
stderr del comando a un grep
comando que lo filtre, y redirigir grep
el stdout de ese comando a stderr.
find args 2> >(grep -Fv 'Permission denied' >&2)
El crédito va a Android Dev por esta idea.
Aunque bash
admite la sustitución de procesos, sh
en Ubuntu es dash
, que no lo hace. Le dará "Error de sintaxis: redirección inesperada" si intenta usar este método, mientras que el método de intercambio de stdout y stderr seguirá funcionando. Además, cuando se bash
ejecuta en modo POSIX , el soporte para la sustitución de procesos está desactivado.
Una situación en la que se bash
ejecuta en modo POSIX es cuando se invoca como sh
1 . Por lo tanto, en un sistema operativo como Fedora donde se bash
proporciona /bin/sh
, o si ha hecho que el /bin/sh
enlace simbólico se señale a bash
usted mismo en Ubuntu, la sustitución del proceso aún no funciona en un sh
script, sin un comando previo para desactivar el modo POSIX. Su mejor opción, si desea utilizar este método en un script, es colocarlo #!/bin/bash
en la parte superior en lugar de #!/bin/sh
, si aún no lo ha hecho.
1 : en esta situación, bash
activa el modo POSIX automáticamente después de ejecutar los comandos en sus scripts de inicio.
Un ejemplo
Es útil poder probar estos comandos. Para hacer esto, creo un tmp
subdirectorio del directorio actual y lo lleno con algunos archivos y directorios, quitando los permisos de uno de ellos para activar un error "Permiso denegado" 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
Uno de los directorios que es accesible incluye un archivo con "Permiso denegado" en su nombre. La ejecución find
sin redireccionamientos o canalizaciones muestra este archivo, pero también muestra el error real "Permiso denegado" para otro directorio que no es accesible:
ek@Io:~/tmp$ find
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find: ‘./b’: Permission denied
La canalización de stdout y stderr grep
y el filtrado de líneas que contienen "Permiso denegado" hace que el mensaje de error desaparezca, pero también oculta el resultado de búsqueda del archivo con esa frase en su nombre:
ek@Io:~/tmp$ find |& grep -Fv 'Permission denied'
.
./a
./a/x
./c
./c/z
./w
./b
find 2>&1 | grep -Fv 'Permission denied'
es equivalente y produce la misma salida.
Los métodos mostrados anteriormente para filtrar "Permiso denegado" solo de mensajes de error, y no de resultados de búsqueda, son exitosos. Por ejemplo, este es el método donde se intercambian stdout y stderr:
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)
produce la misma salida
Puede activar un mensaje de error diferente para asegurarse de que las líneas enviadas a stderr que no contienen el texto "Permiso denegado" aún se permitan. Por ejemplo, aquí he corrido find
con el directorio actual ( .
) como un punto de partida, pero el directorio inexistente foo
como otro:
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
Comprobar que find
la salida estándar sigue siendo una terminal
También podemos ver qué comandos hacen que los caracteres especiales, como las nuevas líneas, se muestren literalmente. (Esto se puede hacer por separado de la demostración anterior, y no necesita estar en el tmp
directorio).
Haga un archivo con una nueva línea en su nombre:
touch $'abc\ndef'
Usualmente usamos directorios como puntos de partida find
, pero los archivos también funcionan:
$ find abc*
abc?def
Tuberías de salida estándar a otro comando hace que el salto de línea ha de ser emitida, literalmente, creando la falsa impresión de dos resultados de búsqueda separadas abc
y def
. Podemos probar eso con cat
:
$ find abc* | cat
abc
def
Redirigir solo stderr no causa este problema:
$ find abc* 2>/dev/null
abc?def
Tampoco cerrarlo:
$ find abc* 2>&-
abc?def
Tubería a grep
sí causa del problema:
$ find abc* |& grep -Fv 'Permission denied'
abc
def
(Reemplazar |&
con 2>&1 |
es equivalente y produce la misma salida).
Intercambiar stdout y stderr y canalizar stdout no causa el problema: el estándar find
stdout se convierte en stderr, que no se canaliza:
$ find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
abc?def
Agrupar ese comando e intercambiar las secuencias de nuevo no causa el problema:
$ (find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
abc?def
(La {
;}
versión produce la misma salida).
El uso de la sustitución de procesos para filtrar stderr tampoco causa el problema:
$ find abc* 2> >(grep -Fv 'Permission denied' >&2)
abc?def