El comando de búsqueda de Linux omite un directorio

El comando de búsqueda de Linux omite un directorio

Tengo ciertas carpetas compartidas de red montadas en el /mediadirectorio.

Quiero asegurarme de que cuando haga algo así sudo find / -name foosiempre omita el /mediadirectorio.

No quiero pasar un parámetro al findcomando... Quiero configurar mi sistema de tal manera que findsiempre por defecto omita /mediael directorio.

Respuesta1

Hay una serie de casos extremos que deben considerarse en esta situación. El primer enfoque find / -path '/media' -prune -o ...es suficiente sólo cuando la ruta de búsqueda es absoluta y comienza con /. El escenario cd / && find * ...nunca coincidirá con la -path '/media'cláusula.

Afortunadamente, el -inumparámetro puede venir al rescate. Los números de inodo son únicos sólo por sistema de archivos montado, por lo que para excluir /medianecesitamos identificar la tupla que consta del sistema de archivos y el número de inodo.

El siguiente script (largo) lo excluirá /mediay, con suerte, captará suficientes casos extremos para que le resulte útil.


#!/bin/bash
#
FIND=/usr/bin/find


# Process prefix arguments
#
opt_H= opt_L= opt_P= opt_D= opt_O=
while getopts 'HLPD:O:' opt
do
    case "$opt" in
        H)      opt_H=-H ;;
        L)      opt_L=-L ;;
        P)      opt_P=-P ;;
        D)      opt_D="-D $OPTARG" ;;
        O)      opt_O="-O $OPTARG" ;;
    esac
done
shift $((OPTIND - 1))


# Find the inode number for /media and its filesystem
#
m_inode=$(stat -c '%i' /media 2>/dev/null)
m_fsys=$(stat -c '%m' /media 2>/dev/null)


# Collect the one or more filesystem roots to search
#
roots=()
while [[ 0 -lt $# && "$1" != -* ]]
do
    roots+=("$1")
    shift
done


# Collect the "find" qualifiers. Some of them need to be at the front
# of the list. Unfortunately.
#
pre_args=() args=()
while [[ 0 -lt $# ]]
do
    # We really ought to list all qualifiers here, but I got tired of
    # typing for an example
    #
    case "$1" in
        -maxdepth)      pre_args+=("$1"); pre_args+=("$2"); shift 2 ;;
        -mindepth)      pre_args+=("$1"); pre_args+=("$2"); shift 2 ;;
        -mount|-xdev)   pre_args+=("$1"); shift ;;
        -depth|-d)      pre_args+=("$1"); shift ;;
        -name|-iname)   args+=("$1"); args+=("$2"); shift 2 ;;
        -path|-ipath)   args+=("$1"); args+=("$2"); shift 2 ;;
        *)              args+=("$1") ; shift ;;
    esac
done
test -z "${args[*]}" && args=('-print')


# Iterate across the collected filesystem roots, attempting to skip
# /media only if the filesystem matches
#
exit_ss=0
for root in "${roots[@]}"
do
    fsys=$(stat -c '%m' "$root" 2>/dev/null)
    if [[ -n "$m_inode" && -n "$m_fsys" && "$fsys" == "$m_fsys" ]]
    then
        # Same filesystem. Exclude /media by inode
        #
        "$FIND" ${opt_H:+"$opt_H"} ${opt_L:+"$opt_L"} \
                ${opt_P:+"$opt_P"} ${opt_O:+"$opt_O"} \
                ${opt_O:+"$opt_O"} "$root" "${pre_args[@]}" \
                \( -inum "$m_inode" -prune \) -o \( "${args[@]}" \)
        ss=$?
        [[ 0 -lt $ss ]] && exit_ss="$ss"
    else
        # Different filesystem so we don't need to worry about /media
        #
        "$FIND" ${opt_H:+"$opt_H"} ${opt_L:+"$opt_L"} \
                ${opt_P:+"$opt_P"} ${opt_O:+"$opt_O"} \
                ${opt_O:+"$opt_O"} "$root" "${pre_args[@]}" \
                "${pre_args[@]}" \( "${args[@]}" \)
        ss=$?
        [[ 0 -lt $ss ]] && exit_ss="$ss"
    fi
done


# All done
#
exit $exit_ss

Respuesta2

Si desea ceñirse al uso "simple" de find(es decir, no múltiples directorios, sin opciones -H -L -D -P -O) y está de acuerdo con usar la -xdevopción, pruebe esta respuesta simple. Esto excluirá todos los sistemas de archivos montados (por ejemplo, también $HOMEsi se montan por separado).

Puede definir una función bash findde modo que no se adapte a otros sistemas de archivos. Pon esto en tu ~/.bashrc(suponiendo que estés usando bash)

find () {
  local path="${1}"
  shift
  command find "${path}" -xdev "${@}"
}

Explicación: Necesitamos usar una función en lugar de un alias porque findes muy exigente con el orden de sus argumentos. El pathdebe ser el primer argumento. Por lo tanto, guardamos el pathen una variable local y lo sacamos de la lista de argumentos ( shift). Luego ejecutamos la búsqueda original command findcon la ruta y todos los argumentos restantes $@. El commandfrente de findasegura que no terminemos con una llamada recursiva.

Con el nuevo ~/.bashrcarchivo, primero debe obtenerlo

source ~/.bashrc

Luego, podrá utilizar la nueva versión de find. Siempre puedes comprobar la definición usando

> type find
find is a function
find ()
{
    local path="${1}";
    shift;
    command find "${path}" -xdev "${@}"
}

Respuesta3

(   set -e -- "$(command -v find)"
    [ -x "${1:?}"  ]
    [ ! -e "$1cmd" ]
    [ ! -L "$1cmd" ]
    mv -- "$1"  "$1cmd"
    cat > "$1"
    chmod +x -- "$1"
)   <<""
#!/bin/sh -f
eval '  exec    "$0cmd" '"${1$(                       # f!'"ing colors
        unset   i L O M rt IFS
        chk()   case   ${O+$2}${2--}    in            # $O must be set or $2
                (-maxdepth"$2") M=      ;;            # unset to match "$2$2"
                ([\(!]"$2"|-*"$2")                    # this is the last match
                        printf  %s${1+%b}%.d\
                               "$rt" \\c 2>&-   &&    # printf fails if ! $1  
                        chk(){  ${1+:} exit; }  ;;    # chk() = !!$1 || exit
                (-?*)   shift   $((OPTIND=1))         # handle -[HLP] for 
                        while   getopts :HLP    O     # path resolution w/
                        do      case    $O${L=} in    # NU$L expansions
                                (P)     unset L ;;    # $[HL]=:- $P=-
                                (\?)    rt= chk ''    # opt unexpected  &&
                                        return  ;;    # abandon parse
                        esac;   done;   unset O ;;    # $O is unset until 
                (${M-${O=?*}})                        # above matches fail
                      ! [ ! -L "${L-$2}" ]      ||    # ! -P ||!! -L ||
                        [ !  / -ef "$2"  ]      ||    # !  / == $2   ||
                        rt=$rt' ! \( -path "${'$i'%/}/media/*" -prune \)'
                esac
        while   chk ${1+$((i+=1)) "$1"}               # loop while args remain
        do      printf ' "${'$i}\"                    # printf args to eval
                shift                                 # shift args away
        done                                          # done
)"

Hay un script contenedor findque insertará algunos argumentos para prohibir que lo real findmire /media/si alguno de los argumentos de su ruta es igual /.

Hay dos partes del guión anterior: está el guión real(que es todo lo que sigue<<""\n) y está el bit de instalación de ejecución única en la parte superior((que es todo lo que está entre el primer par de paréntesis coincidentes )).

instalar

(   set -e -- "$(command -v find)"         #get /path/to/find
    [ -x "${1:?}"  ]                       #else loudly fail
    [ ! -e "$1cmd" ]                       #fail if /path/to/findcmd
    [ ! -L "$1cmd" ]                       #and double-check
    mv -- "$1"  "$1cmd"                    #rename .../find -> .../findcmd
    cat > "$1"                             #copy stdin to .../find
    chmod +x -- "$1"                       #set new find's executable bit
)   <<"" ###stdin

La broca de instalación requiere un poco de cuidado.nopara completarse exitosamente a menos que exista una posibilidad razonable de que pueda hacerlo sin modificar directamente nada en su sistema excepto el nombre de archivo $PATHdel findejecutable; quiere cambiarlo de /path/to/finda /path/to/findcmdy lo intentará si /path/to/findcmdaún no existe. Si sus pruebas resultan verdaderas, y si tiene los permisos adecuados para aplicar los comandos, cambiará el nombre del findejecutable e instalará un nuevo script de shell nombrado finden su lugar.

A partir de entonces, el script instalado dependerá siempre de que el findcmdejecutable renombrado permanezca donde lo dejó.(por lo que probablemente querrás informar esto a tu administrador de paquetes si usas uno)y cada vez que se invoque se reemplazará con $0cmdllamado con todos sus argumentos después de haberlos echado un vistazo. Si no hace lo necesario para que la instalación sea permanente, eventualmente la mayoría de los administradores de paquetes terminarán sobrescribiendo el script instalado con un findbinario recién actualizado en algún momento, por lo que volverá al punto de partida, excepto que También tendrás un findnombre anterior en el directorio findcmddel sistema .../bin

Dados los permisos adecuados y que su sistema no garantiza sorpresas indebidas, todo el script debería poder autoinstalarse con solo copiar y pegar en un símbolo del shell.(aunque necesitarás hacer una DEVOLUCIÓN adicional al final). Si no funciona de esa manera, entonces, al menos, no debería haber ningún daño en el intento.

nuevo hallazgo

#!/bin/sh -f
eval '  exec    "$0cmd" '"${1+$(                      # f!'"ing colors
        unset   i L O M rt IFS
        chk()   case   ${O+$2}${2--}    in            # $O must be set or $2
                (-maxdepth"$2") M=      ;;            # unset to match "$2$2"
                ([\(!]"$2"|-*"$2")                    # this is the last match
                        printf  %s${1+%b}%.d\
                               "$rt" \\c 2>&-   &&    # printf fails if ! $1  
                        chk(){  ${1+:} exit; }  ;;    # chk() = !!$1 || exit
                (-?*)   shift   $((OPTIND=1))         # handle -[HLP] for 
                        while   getopts :HLP    O     # path resolution w/
                        do      case    $O${L=} in    # NU$L expansions
                                (P)     unset L ;;    # $[HL]=:- $P=-
                                (\?)    rt= chk ''    # opt unexpected  &&
                                        return  ;;    # abandon parse
                        esac;   done;   unset O ;;    # $O is unset until 
                (${M-${O=?*}})                        # above matches fail
                      ! [ ! -L "${L-$2}" ]      ||    # ! -P ||!! -L ||
                        [ !  / -ef "$2"  ]      ||    # !  / == $2   ||
                        rt=$rt' ! \( -path "${'$i'%/}/media/*" -prune \)'
                esac
        while   chk ${1+$((i+=1)) "$1"}               # loop while args remain
        do      printf ' "${'$i}\"                    # printf args to eval
                shift                                 # shift args away
        done                                          # done
)}"

Mi primera y más importante regla al escribir un script contenedor es:no intervenir. Si necesitara un programa, intentaría escribir uno, pero como ya tengo un programa que vale la pena ajustar, intentaré dejarlo hacer lo que ya hace sin obstáculos y modificar lo menos posible su comportamiento para lograrlo. mi objetivo final. Esto significa que no debería hacer nada que pueda afectar su entorno de ejecución de ninguna manera que no esté directamente relacionado con el propósito del ajuste. Por lo tanto, no establezco variables, no interpreto argumentos, no toco flujos de E/S y no altero el grupo de procesos del programa empaquetado ni su pid principal. En todo caso, el envoltorio debe ser lo más transitorio y transparente posible.

El script anterior logra este objetivo, más ahora que antes. No estaba satisfecho antes, especialmente con respecto a las resoluciones de rutas, pero creo que lo he solucionado. Para hacerlo correctamente, tuve que realizar un seguimiento [HLP]del estado para poder comparar correctamente los enlaces simbólicos /cuando cualquiera de las opciones -Ho -Leran efectivas y -Pno las negaban. Si la prueba de enlace pasa, se verifica que el argumento actual -efcoincida con el inodo del archivo igual /, lo que significa que prácticamente cualquier nombre /funcionará(para incluir enlaces simbólicos cuando -Ho -Lsean efectivos). Así que me siento mejor con esas cosas y lo configuré para bloquear las búsquedas y /procde forma predeterminada./sys/dev/

Lo que hace especialmente bien es evitar modificar cualquiera de sus estados llamados antes de pasarlo a $0cmd. Se niega deliberadamente a interpretar un conjunto de argumentos que incluye cualquier opción que no esté preparado para manejar y en esos casos pasa el conjunto completo $0cmdsin tocar, por lo que, aunque en esos casos no puede bloquear la búsqueda de ruta, tampoco afecta findel comportamiento de cualquier otra manera. Es por eso que el eval "exec wrapped_program $(arg-handler)"método es el que más prefiero para este tipo de cosas.

nivel superior

De hecho, como se indicó anteriormente, en su nivel superior, todo el script de shell equivale sólo a un comando simple, que le indica que se reemplace con otro ejecutable. Cualquier trabajo realizado se realiza dentro del subshell $(de sustitución de comandos ), y todo su estado (modificado o no) está completamente localizado. El propósito detrás evalde todo esto es obtener una segunda mirada a los argumentos del guión sin necesidad de afectarlos innecesariamente, y de eso se trata este envoltorio.

Cuando el $(comando sub )haya hecho su trabajo, el execcomando 'd resultante será:

exec "$0cmd" "${1}" ... ! \( -path "${[num]%/}/media/*" -prune \) "${2}" ...

... donde se hace referencia a todos los argumentos originales, si los hay, en orden y por número en su forma original y sin modificaciones.(incluso argumentos nulos)además de los seis( !, \(, -path, "${[num]%/}/media/*", -prune, \))inserciones, un conjunto de las cuales ocurre para cada / -ef "${num}"prueba exitosa durante el escaneo de arg. De lo contrario será simplemente:

exec "$0cmd" "${1}" "${2}" "${3}" "${4}" ...

...donde se hace referencia a todos los argumentos originales de la misma manera sin ninguna inserción.

Entonces, las dos únicas modificaciones posibles que este contenedor puede realizar en el entorno de su destino empaquetado son estas:

  • Altera el nombre del proceso del suyo propio a su nombre +cmd. Esto siempre sucede.

  • Puede inyectar seis argumentos por coincidencia raíz en la lista de aquellos con los que fue llamado.

Siempre que la primera modificación se considere aceptable(aunque es evitable), aquí hay un único punto de falla con respecto a la modificación del comportamiento, y es si la inserción del argumento es válida o no. Cualquier error asociado con la invocación simplemente está fuera de alcance y debe ser manejado por el destino del ajuste, y este contenedor intenta ocuparse de su negocio.

controlador de argumentos

Dentro de la sustitución de comandos, primero inicio vars para desarmar porque la ""cadena es la mejor manera denocoincidir con una ruta cuando sea necesario. Luego declaro la chk()función y luego la llamo para cada iteración del whilebucle que se incrementará $ien uno para cada uno de los argumentos de invocación del script. Imprimirá cada incremento $ientre comillas y llaves precedido por un espacio y un signo de dólar en la salida estándar del comando sub:

printf ' "${'$i}\"

...

 "${1}"

Realiza un bucle sobre las llamadas a chk()las que obtiene una copia de sus argumentos por iteración, luego shiftlas elimina hasta que no queda ninguna y el bucle se completa. chk()compara su argumento con sus patrones y toma las acciones apropiadas:

  • (-maxdepth"$2") M= ;;

    • Cuando $Mse establece, el último patrón solo puede coincidir con una cadena nula que solo puede fallar en las pruebas de comparación de ruta posteriores de su bloque, y asírt=$rt+!\(y así sucesivamente nunca ocurre en ese caso. De lo contrario no se hace nada.

    • La especificación POSIX solo requiere -[HL]ser reconocida antes de cualquier [...path...]operando, y los demás no están especificados. Esto es lo que dice sobre cuáles son [...path...]operandos y cuáles son operandos de prueba:

      El primer operando y los operandos subsiguientes hasta, pero sin incluir, el primer operando que comienza con a , o es a ! o a (, se interpretarán como [...path...]operandos. Si el primer operando comienza con a , o es a ! o a (, el comportamiento no se especifica. Cada operando de ruta es un nombre de ruta de un punto de partida en la jerarquía de archivos.

  • ([\(!]"$2"|-*"$2")

    • El argumento actual es un (par izquierdo único, o una !explosión, o comienza con un -*guión pero no lo es -maxdepthy el último patrón ha coincidido al menos una vez.

    • printf %s ${1+%b}%.d "$rt" \\c 2>&- &&

      • Escriba el valor de $rt(si lo hay) en el estándar de sustitución del comando seguido de una escritura exitosa del \c %bescape de longitud cero, o una conversión fallida a %.decimal del mismo y con la misma longitud si $1no está establecido y el final de los argumentos ha sido alcanzó. Este fallo pondrá fin al whileciclo.
    • chk(){ ${1+:} exit; }

      • Si printftiene éxito, entonces chk()habrá hecho su único intento de modificar cualquier argumento. A partir de este punto, el whilebucle puede continuar manejando e imprimiendo el resto de los argumentos, pero chk()no hará nada en absoluto hasta que se agoten todos, momento en el que solo será exitel subshell. Y así, una vez que el segundo patrón coincide aunque sea una vez, ninguno de los demás vuelve a coincidir.
  • (-?*)

    • El argumento actual tiene al menos dos caracteres y comienza con un guión. Este patrón esmásexclusivo que el -*"$2"patrón anterior una vez $Oestablecido y, por lo tanto, solo puede coincidir hasta que al menos un único argumentonoIgualalo. De esta manera, todas las opciones iniciales se dividirán getoptsy compararán [HPL]. Si alguna opción inicial no se ajusta a ese patrón, la función se llama a sí misma de forma recursiva para coincidir con el patrón anterior y redefinirla chk(). De esta manera, cualquier secuencia de argumentos que no se maneje explícitamente simplemente se pasa palabra por palabra y findcmdhace lo que quiera con los resultados.

    • Para cada opción inicial que coincida, -[HL]la variable de bandera $Lse establece en la cadena nula. Y para cada uno de los que coinciden -P $Les unset.

  • (${M-${O=?*}})

    • El primer argumento que aparezca y que no coincida -?*provocará $Oque se establezca el ?*patrón. A partir de entonces, cualquiera de los dos primeros patrones puede coincidir ${O+$2}${2--}. Si ever -maxdepth$2coincide y M=se establece en la cadena nula, este patrón nunca más podrá coincidir con otro argumento no nulo, y solo se requiere una única coincidencia del segundo patrón para cesar todos los intentos de hacer coincidir cualquiera de ellos.

    • Cualquier argumento no nulo que ocurra después de la primera secuencia de -[HLP]opciones y antes de que otro -*argumento [\(?!]coincida con este patrón y se prueba para determinar la resolución de la ruta. Si $Lno está configurado, la ! ! -L "${L-$2}"prueba pasará si $2es un enlace simbólico o un nombre de ruta no válido, pero de lo contrario falla invariablemente porque ningún nombre de ruta puede coincidir con la ${L=}cadena nula.

    • Sólo aquellos argumentos que no pasan la prueba anterior se verifican para una !coincidencia de inodo negada /y cualquier argumento que no pasa ambas pruebas resulta en $rtser establecido en sí mismo más la ! \( -path "${[num]%/}/media/* -prune \)cadena que no se escribe hasta que el segundo patrón coincida o se alcance el final de los argumentos, lo que sea viene primero.

información relacionada