Cuando uso find
para ver todos los archivos pdf en el /home
directorio, 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 denied
resultados 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 ~/.dbus
2>&-
. 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
stderr
lugar de hacerlostdout
.Prueba esto:
El
2>&1
redirige la salida destderr
astdout
, por lo quegrep -v
puede hacer su trabajo. (Por defecto,|
solo tuberíasstdout
y nostderr
).fuente
2>&1
... No soy un experto en bash, así que si eso es incorrecto, entonces díganlo :)bash
en Ubuntu lo tiene, excepto en modo POSIX . Creo que es la mejor solución:access denied
todavía aparecerá un archivo con un nombre malicioso .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:
(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/null
o 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,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 existea
y noc
existeb
,find a b c -name x
imprime los resultadosa
, luego "No existe tal archivo o directorio"b
y luego los resultadosc
.La combinación de stdout y stderr juntos en stdout y canalizarlo a
grep
algú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). Algunasfind
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.?
se imprimen en su lugar.grep
) hace quefind
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 quefind
es terminal La detección está destinada a prevenir.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 (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
grep
para 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 deargs
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.
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.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
find
stderr del comando a ungrep
comando que lo filtre, y redirigirgrep
el 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
bash
admite la sustitución de procesos,sh
en 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 sebash
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 comosh
1 . Por lo tanto, en un sistema operativo como Fedora donde sebash
proporciona/bin/sh
, o si ha hecho que el/bin/sh
enlace simbólico se señale abash
usted mismo en Ubuntu, la sustitución del proceso aún no funciona en unsh
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
.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: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: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
find
con el directorio actual (.
) como un punto de partida, pero el directorio inexistentefoo
como otro:Comprobar que
find
la 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
tmp
directorio).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
abc
ydef
. Podemos probar eso concat
:Redirigir solo stderr no causa este problema:
Tampoco cerrarlo:
Tubería a
grep
sí 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
find
stdout 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