Tenho certas pastas de compartilhamento de rede montadas no /media
diretório.
Quero ter certeza de que, quando fizer algo assim, sudo find / -name foo
sempre pule o /media
diretório.
Não quero passar parâmetro para o find
comando... Quero configurar meu sistema de forma que find
sempre por padrão pule /media
diretório.
Responder1
Há uma série de casos extremos que precisam ser considerados nesta situação. A primeira abordagem find / -path '/media' -prune -o ...
é suficiente apenas quando o caminho de pesquisa é absoluto e começa com /
. O cenário cd / && find * ...
nunca corresponderá à -path '/media'
cláusula.
Felizmente, o -inum
parâmetro pode ajudar. Os números dos inodes são exclusivos apenas por sistema de arquivos montado, portanto, para excluir, /media
precisamos identificar a tupla que consiste no sistema de arquivos e no número do inode.
O script (longo) a seguir excluirá /media
para você, esperançosamente capturando casos extremos suficientes para serem úteis.
#!/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
Responder2
Se você deseja manter o uso "simples" de find
(ou seja, sem vários diretórios, sem opções -H -L -D -P -O
) e concorda em usar a -xdev
opção, tente esta resposta simples. Isto excluirá todos os sistemas de arquivos montados (por exemplo, também $HOME
se forem montados separadamente).
Você pode definir uma função bash find
de forma que ela não seja compatível com outros sistemas de arquivos. Coloque isso no seu ~/.bashrc
(supondo que você esteja usando bash
)
find () {
local path="${1}"
shift
command find "${path}" -xdev "${@}"
}
Explicação: Precisamos usar uma função em vez de um alias porque find
é muito exigente quanto à ordem de seus argumentos. Deve path
ser o primeiro argumento. Portanto, salvamos o path
em uma variável local e o retiramos da lista de argumentos ( shift
). Em seguida, executamos o find original command find
com o caminho e todos os argumentos restantes $@
. O command
in front of find
garante que não terminemos com uma chamada recursiva.
Com o novo ~/.bashrc
arquivo, você precisa obtê-lo primeiro
source ~/.bashrc
Então, você pode usar a nova versão do find
. Você sempre pode verificar a definição usando
> type find
find is a function
find ()
{
local path="${1}";
shift;
command find "${path}" -xdev "${@}"
}
Responder3
( 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
)"
Existe um script wrapper find
que irá inserir alguns argumentos para proibir o real find
de procurar /media/
se algum de seus argumentos de caminho for igual /
.
Existem duas partes do script acima: existe o script real(que é tudo o que segue<<""\n
) e há o bit de instalação executado uma vez na parte superior(que é tudo entre o primeiro par de (
parênteses )
).
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
A parte de instalação exige um pouco de cuidadonãopara ser concluído com sucesso, a menos que haja uma chance razoável de fazê-lo sem modificar diretamente nada em seu sistema, exceto o nome do arquivo do seu executável $PATH
'd find
- ele deseja alterar isso de /path/to/find
para /path/to/findcmd
e fará a tentativa se /path/to/findcmd
ainda não existir. Se seus testes forem verdadeiros - e se você tiver permissões apropriadas para aplicar os comandos - ele renomeará o find
executável e instalará um novo script de shell nomeado find
em seu lugar.
O script instalado dependerá para sempre do findcmd
executável renomeado permanecendo onde o deixou(então você provavelmente desejará informar seu gerenciador de pacotes sobre isso, se usar um)e cada vez que for invocado, ele se substituirá por $0cmd
call com todos os seus argumentos depois de dar uma olhada neles. Se você não fizer o que for necessário para tornar a instalação permanente, eventualmente a maioria dos gerenciadores de pacotes acabará sobrescrevendo o script instalado com um find
binário recém-atualizado em algum momento, e assim você estará de volta ao ponto de partida, exceto que você também terá um find
nome mais antigo findcmd
no ../bin
diretório do sistema.
Dadas as permissões apropriadas e que seu sistema não garanta surpresas indevidas, todo o script deve ser auto-instalável com um copiar e colar em um prompt de shell(embora você precise fazer um RETURN extra no final). Se não funcionar dessa maneira, então, pelo menos, não deverá haver nenhum dano na tentativa.
recém-descoberto
#!/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
)}"
Minha regra principal ao escrever um script wrapper é:sem intervenção. Se eu precisasse de um programa, tentaria escrever um, mas como já tenho um programa que vale a pena embrulhar, tentarei deixá-lo fazer o que já faz sem impedimentos e modificar o mínimo possível seu comportamento para alcançar meu objetivo final. Isso significa que não devo fazer nada que possa afetar seu ambiente de execução de forma que não esteja diretamente relacionado ao propósito do wrap. Portanto, não defino variáveis, não interpreto argumentos, não toco em fluxos de E/S e não altero o grupo de processos do programa empacotado ou seu pid pai. Em todas as coisas, o invólucro deve ser tão transitório e transparente quanto possível.
O script acima atinge esse objetivo, mais agora do que antes. Eu não estava satisfeito antes - especialmente em relação às resoluções de caminhos - mas acredito que já resolvi isso. Para fazer isso corretamente, tive que rastrear [HLP]
o estado para poder comparar corretamente os links simbólicos com /
quando uma das opções -H
ou -L
era eficaz e -P
não os negava. Se o teste do link for aprovado, o argumento atual será verificado para uma -ef
correspondência de inode de arquivo igual /
- o que significa que praticamente qualquer nome /
funcionará(para incluir links simbólicos quando -H
ou -L
forem eficazes). Então , me sinto melhor com essas coisas e configurei-o para bloquear /proc
ee /sys
de pesquisas por padrão./dev
/
O que ele faz especialmente bem é evitar modificar qualquer estado chamado antes de passá-lo para $0cmd
. Ele se recusa claramente a interpretar um conjunto de argumentos que inclui qualquer opção com a qual não está preparado para lidar e, nesses casos, passa todo o conjunto para $0cmd
intocado e, portanto, embora nesses casos possa não bloquear a busca do caminho, também não afeta. find
comportamento de qualquer outra forma. É por esta razão que o eval "exec wrapped_program $(arg-handler)"
método é o que mais prefiro para este tipo de coisas.
nível superior
Na verdade, como indicado acima, em seu nível superior, todo o shell-script equivale apenas a um único comando simples - que diz para ele se substituir por outro executável. Qualquer trabalho realizado é feito dentro do subshell $(
de substituição de comando )
, e todo o seu estado - modificado ou não - é totalmente localizado. O objetivo por trás eval
de tudo é dar uma segunda olhada nos argumentos do script sem precisar afetá-los desnecessariamente - e é disso que se trata este wrapper.
Quando o $(
comando sub )
tiver feito seu trabalho, o exec
comando 'd resultante será:
exec "$0cmd" "${1}" ... ! \( -path "${[num]%/}/media/*" -prune \) "${2}" ...
...onde todos os argumentos originais - se houver - são referenciados em ordem e por número em sua forma original e inalterada(mesmo argumentos nulos)além dos seis( !
, \(
, -path
, "${[num]%/}/media/*"
, -prune
, \)
)inserções, um conjunto das quais ocorre para cada / -ef "${num}"
teste bem-sucedido durante a varredura arg. Caso contrário, será simplesmente:
exec "$0cmd" "${1}" "${2}" "${3}" "${4}" ...
...onde todos os argumentos originais são referenciados da mesma maneira, sem nenhuma inserção.
Portanto, as únicas duas modificações possíveis que este wrapper pode fazer no ambiente do seu destino empacotado são estas:
Ele altera o nome do processo do seu próprio para o seu nome +
cmd
. Isso sempre acontece.Pode injetar seis argumentos por correspondência de raiz na lista daqueles com os quais foi chamado.
Desde que a primeira alteração seja considerada aceitável(embora seja evitável), há um único ponto de falha aqui em relação à modificação do comportamento - e é se a inserção do argumento é válida ou não. Quaisquer erros associados à invocação estão simplesmente fora do escopo e devem ser tratados pelo destino do wrapper, e esse wrapper tenta cuidar de seus negócios.
manipulador de argumentos
Dentro da substituição do comando, primeiro inicio vars para unset porque a ""
string é a melhor maneira denãocombine um caminho quando necessário. Em seguida, declaro a chk()
função e depois a chamo para cada iteração do while
loop que será incrementado $i
em um para cada um dos argumentos de invocação do script. Ele imprimirá cada incremento $i
entre aspas e colchetes precedido por um espaço e um cifrão no stdout do subcomando:
printf ' "${'$i}\"
...
"${1}"
Ele faz um loop nas chamadas para chk()
as quais obtém uma cópia de seus argumentos por iteração e, em seguida, shift
afasta-o até que não reste nenhum e o loop seja concluído. chk()
compara seu argumento com seus padrões e toma as ações apropriadas:
(-maxdepth"$2") M= ;;
Quando
$M
é definido, o último padrão só pode corresponder a uma string nula que só pode falhar nos testes subsequentes de comparação de caminho do seu bloco, e assimrt=$rt+!\(
e assim por diante nunca ocorre nesse caso. Caso contrário, nada será feito.A especificação POSIX só precisa
-[HL]
ser reconhecida antes de qualquer[...path...]
operando, e quaisquer outros não são especificados. Aqui está o que diz sobre quais são[...path...]
operandos e quais são operandos de teste:O primeiro operando e os operandos subsequentes até, mas não incluindo, o primeiro operando que começa com a
−
, ou é a!
ou a(
, devem ser interpretados como[...path...]
operandos. Se o primeiro operando começar com a−
, ou for a!
ou a(
, o comportamento não será especificado. Cada operando de caminho é um nome de caminho de um ponto inicial na hierarquia de arquivos.
([\(!]"$2"|-*"$2")
O argumento atual é um único
(
parêntese esquerdo, ou um!
estrondo, ou começa com um-*
travessão, mas não é-maxdepth
e o último padrão foi correspondido pelo menos uma vez.printf %s ${1+%b}%.d "$rt" \\c 2>&- &&
- Escreva o valor de
$rt
- se houver - no padrão de substituição do comando, seguido por uma gravação de comprimento zero bem-sucedida do\c
%b
escape ou uma conversão falhada para%.d
ecimal do mesmo e com o mesmo comprimento se$1
não estiver definido e o final dos argumentos tiver sido alcançado. Essa falha encerrará owhile
loop.
- Escreva o valor de
chk(){ ${1+:} exit; }
- Se
printf
for bem sucedido, entãochk()
terá feito a sua única tentativa de modificar quaisquer argumentos. Deste ponto em diante, owhile
loop pode continuar a manipular e imprimir o restante dos argumentos, maschk()
não fará nada até que todos eles estejam esgotados, momento em que será apenasexit
o subshell. E assim, uma vez que o segundo padrão corresponda pelo menos uma vez, nenhum dos outros corresponderá novamente.
- Se
(-?*)
O argumento atual tem pelo menos dois caracteres e começa com um travessão. Este padrão émaisexclusivo do que o
-*"$2"
padrão acima dele$O
é definido uma vez e, portanto, só pode corresponder até pelo menos um único argumentonãocombine com isso. Desta forma, todas as opções iniciais serão divididasgetopts
e comparadas[HPL]
. Se alguma opção inicial não se enquadrar nesse padrão, a função chama a si mesma recursivamente para corresponder ao padrão acima dela e redefinirchk()
. Desta forma, qualquer sequência arg que não seja explicitamente tratada é meramente passada literalmente efindcmd
faz o que quiser com os resultados.Para cada opção inicial que corresponda
-[HL]
à variável flag$L
é definida como a string nula. E para cada um daqueles que correspondem-P
$L
éunset
.
(${M-${O=?*}})
O primeiro argumento que não corresponder
-?*
irá acionar$O
a definição do?*
padrão. Depois disso, qualquer um dos dois primeiros padrões pode corresponder${O+$2}${2--}
. Se ever-maxdepth$2
for correspondido eM=
definido como a string nula, esse padrão nunca mais poderá corresponder a outro argumento não nulo, e apenas uma única correspondência do segundo padrão será necessária para cessar todas as tentativas de correspondência de qualquer um deles.Qualquer argumento não nulo que ocorre após a primeira sequência de
-[HLP]
opções e antes de outro argumento-*
ou[\(?!]
corresponde a este padrão e é testado para resolução de caminho. Se$L
não estiver definido, o! ! -L "${L-$2}"
teste será aprovado se$2
for um link simbólico ou um nome de caminho inválido, mas caso contrário, invariavelmente falhará porque nenhum nome de caminho pode corresponder à${L=}
cadeia nula.Somente aqueles argumentos que falham no teste anterior são verificados quanto a uma
!
correspondência de inode negada/
e qualquer argumento que falha em ambos os testes resulta em$rt
ser definido para si mesmo mais a! \( -path "${[num]%/}/media/* -prune \)
string que não é escrita até que o segundo padrão corresponda ou o final dos argumentos seja alcançado, o que for vem primeiro.