Extracción de "Acceso denegado" Líneas

9

Cuando uso findpara ver todos los archivos pdf en el /homedirectorio, estoy viendo access denied. Para eliminarlos intenté:

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

Sin embargo, el resultado es el mismo. ¿Cómo puedo deshacerme de estas líneas?

solfish
fuente

Respuestas:

19

Lo que intentó no funcionó porque los access deniedresultados son errores y se enviaron en STDERR en lugar de STDOUT al que se canaliza grep.

Puede evitar ver esos errores redirigiendo solo STDERR

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

O como comentó David Foerster , podemos cerrar de manera más sucinta STDERR

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

Sin embargo, sospecho que en realidad solo desea buscar su casa en lugar de la de otros usuarios, por lo que tal vez realmente quiera

find ~ -iname "*.pdf"

Si eso arroja errores, puede haber algunas propiedades incorrectas en su configuración local, que debe investigar.

Zanna
fuente
3
Grrr, ¿por qué la gente siempre me gana por 30 segundos? : \
You'reAGitForNotUsingGit
find: “/home/ihsan/.gvfs”: acceso denegado find: “/home/ihsan/.dbus”: acceso denegado, para el comando ~
solfish
¿hay algo mal para eso? sí, también quiero el directorio de inicio de otros usuarios, que también he creado para probarlo
solfish
2
@solfish Hasta donde yo sé, esos archivos deberían ser tuyos. Es posible que deseesudo chown $USER: ~/.gvfs ~/.dbus
Zanna
1
Debería ser suficiente para cerrar stderr con 2>&-. GNU find no terminará si intenta escribir mensajes de error en un descriptor de archivos disfuncional. Para los problemas de propiedad sudo chown -R $USER: ...sería más efectivo en caso de que más archivos dentro de los cuales no sean propiedad $USER.
David Foerster
8

El acceso denegado probablemente se esté imprimiendo en stderrlugar de hacerlo stdout.

Prueba esto:

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

El 2>&1redirige la salida de stderra stdout, por lo que grep -vpuede hacer su trabajo. (Por defecto, |solo tuberías stdouty no stderr).

You'reAGitForNotUsingGit
fuente
pero para esto 2> & 1 significa si existe stderr enviar a stdout?
solfish
@solfish Sí, ese es exactamente el punto :)
You'reAGitForNotUsingGit
lo que no entiendo es antes de "|" como una salida; tenemos solo stderr ¿verdad? y después de "|" como entrada tenemos esto
solfish
@solfish Bueno, me encontré con este problema hace aproximadamente un año y medio, y pude solucionarlo usando un método diferente . Pero luego, un comentario debajo de mi respuesta sugirió simplemente usar 2>&1... No soy un experto en bash, así que si eso es incorrecto, entonces díganlo :)
You'reAGitForNotUsingGit
@AndroidDev Sugiero agregar ese método diferente a esta respuesta como alternativa. La crítica de Etan Reisner fue que la sustitución del proceso no es portátil. Pero bashen Ubuntu lo tiene, excepto en modo POSIX . Creo que es la mejor solución: access deniedtodavía aparecerá un archivo con un nombre malicioso .
Eliah Kagan
4

Probablemente quiere decir "Permiso denegado", que es lo que finden 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/nullo 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, findinforma "No existe tal archivo o directorio" si no existe un punto de partida. Con múltiples puntos de partida, findaún puede devolver algunos resultados útiles y parece funcionar. Por ejemplo, si existe ay no cexiste b, find a b c -name ximprime los resultados a, luego "No existe tal archivo o directorio" by luego los resultados c.

La combinación de stdout y stderr juntos en stdout y canalizarlo a grepalgú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 findacciones, incluida la -printacció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 findya 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 findes terminal La detección está destinada a prevenir.
  • Consulte la sección INUSUAL FILENAMES de man findpara 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 findimplementació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 greppara 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 argsarriba.

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>&2redirige 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 findstderr del comando a un grepcomando que lo filtre, y redirigir grepel 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, shen 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 bashejecuta en modo POSIX , el soporte para la sustitución de procesos está desactivado.

Una situación en la que se bashejecuta en modo POSIX es cuando se invoca como sh1 . Por lo tanto, en un sistema operativo como Fedora donde se bashproporciona /bin/sh, o si ha hecho que el /bin/shenlace simbólico se señale a bashusted mismo en Ubuntu, la sustitución del proceso aún no funciona en un shscript, 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, bashactiva 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 tmpsubdirectorio 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 findsin 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 grepy 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 findcon el directorio actual ( .) como un punto de partida, pero el directorio inexistente foocomo 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 findla 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 tmpdirectorio).

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 abcy 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 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 findstdout 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
Eliah Kagan
fuente