Tengo ciertas carpetas compartidas de red montadas en el /media
directorio.
Quiero asegurarme de que cuando haga algo así sudo find / -name foo
siempre omita el /media
directorio.
No quiero pasar un parámetro al find
comando... Quiero configurar mi sistema de tal manera que find
siempre por defecto omita /media
el 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 -inum
parámetro puede venir al rescate. Los números de inodo son únicos sólo por sistema de archivos montado, por lo que para excluir /media
necesitamos identificar la tupla que consta del sistema de archivos y el número de inodo.
El siguiente script (largo) lo excluirá /media
y, 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 -xdev
opción, pruebe esta respuesta simple. Esto excluirá todos los sistemas de archivos montados (por ejemplo, también $HOME
si se montan por separado).
Puede definir una función bash find
de 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 find
es muy exigente con el orden de sus argumentos. El path
debe ser el primer argumento. Por lo tanto, guardamos el path
en una variable local y lo sacamos de la lista de argumentos ( shift
). Luego ejecutamos la búsqueda original command find
con la ruta y todos los argumentos restantes $@
. El command
frente de find
asegura que no terminemos con una llamada recursiva.
Con el nuevo ~/.bashrc
archivo, 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 find
que insertará algunos argumentos para prohibir que lo real find
mire /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 $PATH
del find
ejecutable; quiere cambiarlo de /path/to/find
a /path/to/findcmd
y lo intentará si /path/to/findcmd
aún no existe. Si sus pruebas resultan verdaderas, y si tiene los permisos adecuados para aplicar los comandos, cambiará el nombre del find
ejecutable e instalará un nuevo script de shell nombrado find
en su lugar.
A partir de entonces, el script instalado dependerá siempre de que el findcmd
ejecutable 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 $0cmd
llamado 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 find
binario recién actualizado en algún momento, por lo que volverá al punto de partida, excepto que También tendrás un find
nombre anterior en el directorio findcmd
del 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 -H
o -L
eran efectivas y -P
no las negaban. Si la prueba de enlace pasa, se verifica que el argumento actual -ef
coincida con el inodo del archivo igual /
, lo que significa que prácticamente cualquier nombre /
funcionará(para incluir enlaces simbólicos cuando -H
o -L
sean efectivos). Así que me siento mejor con esas cosas y lo configuré para bloquear las búsquedas y /proc
de 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 $0cmd
sin tocar, por lo que, aunque en esos casos no puede bloquear la búsqueda de ruta, tampoco afecta find
el 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 eval
de 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 exec
comando '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 while
bucle que se incrementará $i
en uno para cada uno de los argumentos de invocación del script. Imprimirá cada incremento $i
entre 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 shift
las 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
$M
se 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-maxdepth
y 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
%b
escape de longitud cero, o una conversión fallida a%.d
ecimal del mismo y con la misma longitud si$1
no está establecido y el final de los argumentos ha sido alcanzó. Este fallo pondrá fin alwhile
ciclo.
- Escriba el valor de
chk(){ ${1+:} exit; }
- Si
printf
tiene éxito, entonceschk()
habrá hecho su único intento de modificar cualquier argumento. A partir de este punto, elwhile
bucle puede continuar manejando e imprimiendo el resto de los argumentos, perochk()
no hará nada en absoluto hasta que se agoten todos, momento en el que solo seráexit
el subshell. Y así, una vez que el segundo patrón coincide aunque sea una vez, ninguno de los demás vuelve a coincidir.
- Si
(-?*)
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$O
establecido y, por lo tanto, solo puede coincidir hasta que al menos un único argumentonoIgualalo. De esta manera, todas las opciones iniciales se dividirángetopts
y 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 redefinirlachk()
. De esta manera, cualquier secuencia de argumentos que no se maneje explícitamente simplemente se pasa palabra por palabra yfindcmd
hace lo que quiera con los resultados.Para cada opción inicial que coincida,
-[HL]
la variable de bandera$L
se establece en la cadena nula. Y para cada uno de los que coinciden-P
$L
esunset
.
(${M-${O=?*}})
El primer argumento que aparezca y que no coincida
-?*
provocará$O
que se establezca el?*
patrón. A partir de entonces, cualquiera de los dos primeros patrones puede coincidir${O+$2}${2--}
. Si ever-maxdepth$2
coincide yM=
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$L
no está configurado, la! ! -L "${L-$2}"
prueba pasará si$2
es 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$rt
ser 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.