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?
                    
                        command-line
                                bash
                                find
                                
                    
                    
                        solfish
fuente
                
                fuente

Respuestas:
Lo que intentó no funcionó porque los
access deniedresultados son errores y se enviaron en STDERR en lugar de STDOUT al que se canalizagrep.Puede evitar ver esos errores redirigiendo solo STDERR
O como comentó David Foerster , podemos cerrar de manera más sucinta STDERR
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
Si eso arroja errores, puede haber algunas propiedades incorrectas en su configuración local, que debe investigar.
fuente
sudo chown $USER: ~/.gvfs ~/.dbus2>&-. GNU find no terminará si intenta escribir mensajes de error en un descriptor de archivos disfuncional. Para los problemas de propiedadsudo chown -R $USER: ...sería más efectivo en caso de que más archivos dentro de los cuales no sean propiedad$USER.El acceso denegado probablemente se esté imprimiendo en
stderrlugar de hacerlostdout.Prueba esto:
El
2>&1redirige la salida destderrastdout, por lo quegrep -vpuede hacer su trabajo. (Por defecto,|solo tuberíasstdouty nostderr).fuente
2>&1... No soy un experto en bash, así que si eso es incorrecto, entonces díganlo :)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 .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:
(Por lo general, desea pasar algunos argumentos a
find. Esos van antes de la primera redirección3>&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 con2>&--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 existeay nocexisteb,find a b c -name ximprime los resultadosa, luego "No existe tal archivo o directorio"by luego los resultadosc.La combinación de stdout y stderr juntos en stdout y canalizarlo a
grepalgún otro comando para filtrarlo, como con2>&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). Algunasfindacciones, 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.?se imprimen en su lugar.grep) hace quefindya 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 quefindes terminal La detección está destinada a prevenir.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 (incluidosbash) admiten la|&canalización de ambas secuencias, o puede redirigir stderr a stdout primero con2>&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: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 deargsarriba.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.
3>&1redirige 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>&3redirige stderr al descriptor de archivo 3, que es el stdout original.3>&-cierra el descriptor de archivo 3, que ya no es necesario.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:Es la agrupación, no específicamente la subshell, lo que hace que esto funcione. Si lo prefiere, puede usar
{;}: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 ungrepcomando que lo filtre, y redirigirgrepel stdout de ese comando a stderr.El crédito va a Android Dev por esta idea.
1>&2. He usado>&2, que es equivalente ( relacionado ). Usa lo que quieras.Aunque
bashadmite la sustitución de procesos,shen Ubuntu esdash, 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 sebashejecuta 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 comosh1 . Por lo tanto, en un sistema operativo como Fedora donde sebashproporciona/bin/sh, o si ha hecho que el/bin/shenlace simbólico se señale abashusted mismo en Ubuntu, la sustitución del proceso aún no funciona en unshscript, 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/bashen 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.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: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: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:
find args 2> >(grep -Fv 'Permission denied' >&2)produce la misma salidaPuede 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 inexistentefoocomo otro:Comprobar que
findla salida estándar sigue siendo una terminalTambié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:
Usualmente usamos directorios como puntos de partida
find, pero los archivos también funcionan: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
abcydef. Podemos probar eso concat:Redirigir solo stderr no causa este problema:
Tampoco cerrarlo:
Tubería a
grepsí causa del problema:(Reemplazar
|&con2>&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:Agrupar ese comando e intercambiar las secuencias de nuevo no causa el problema:
(La
{;}versión produce la misma salida).El uso de la sustitución de procesos para filtrar stderr tampoco causa el problema:
fuente