
Estou tentando escrever um bash
script que pesquisa o conteúdo dos arquivos em uma árvore de diretórios especificada em busca da presença de uma substring especificada.
Usar grep
a função recursiva por si só não é suficiente, pois potencialmente preciso iterar no /
diretório (e em todos os subdiretórios) de um sistema, o que faz com que grep
fique sem memória e aborte. Portanto, decidi obter uma lista de todos os diretórios e subdiretórios na árvore de diretórios especificada usando find
as seguintes variáveis que denotam argumentos passados para o script.
searchdir=$HOME # passed in a script argument
searchstr="secret" # passed in a script argument
Eu chamo o find
utilitário e armazeno a saída em um arquivo temporário.
TF=$(mktemp)
find ${searchdir} -type d 1>$TF 2>/dev/null
Com a lista de todos os diretórios do arquivo temporário, procedo a iterar sobre as linhas deste arquivo utilizando um while-do
loop com a intenção de realizar uma busca em todos os arquivos de cada diretório. Para grep
, eu uso o formato dos parâmetros fornecidos emesta respostapara pesquisar todos os arquivos, inclusive os ocultos, em um único diretório.
cat $TF | while read line || [[ -n $line ]];
do
grepdir="${line}/{*,.*}"
grep -sHn "${searchstr}" ${grepdir}
done
... no entanto, esse código não produz saída.
Eu verifiquei isso...
O ${TF}
contém a lista correta de todos os diretórios. A saída da ${grepdir}
variável fornece a saída que espero encontrar.
/home/user/{*,.*}
/home/user/.ssh/{*,.*}
/home/user/test/{*,.*}
# ... and so on
Se eu executar o grep
comando com um diretório codificado, especialmente o ~/test/
diretório que contém dois arquivos de teste com a string que ele deve encontrar
grep -sHn "${searchstr}" /home/user/test/{*,.*}
... gera corretamente os dois arquivos que contêm a substring "segredo".
/home/user/test/asdf:7:secret
/home/user/test/test.txt:5:asdfasfdsecretaasdfafd
Um formato que funciona para mim é aquele originalmente mencionado noresposta discutindo o uso recursivo degrep
. Se eu fizer isso:
cat $TF | while read line || [[ -n $line ]];
do
grep -rn "${line}" -e "${searchstr}"
done
... Recebo alguma saída (tecnicamente correta, mas com muitas entradas duplicadas), mas como grep
está processando os diretórios recursivamente e tenho uma lista de todos os diretórios, devo obter os mesmos resultados muitas vezes e em diretórios como como o diretório raiz mencionado acima grep
falhará completamente, e é isso que estou tentando evitar.
Provavelmente também devo mencionar que meus hacks desesperados para fazê-lo funcionar, como passar $(echo "${grepdir}")
como parâmetro, também não levaram a nenhum resultado.
Provavelmente há um equívoco em meu pensamento ou compreensão de bash
. Não deveria bash
expandir a ${grepdir}
variável antes de fazer uma chamada para grep
? Onde meu script está errado?
Responder1
Regra nº 1: quando um comando ou script não está fazendo o que você deseja,
veja as mensagens de erro. Não os jogue dentro /dev/null
.
Você está recebendo mensagens de erro como
grep: /home/user/{*,.*}: No such file or directory
grep: /home/user/.ssh/{*,.*}: No such file or directory
grep: /home/user/test/{*,.*}: No such file or directory
mas você não os está vendo.
Se olharmosfestança(1), Nós vemos
A expansão é executada na linha de comando após ser dividida em palavras. Existem sete tipos de expansão realizados: expansão de chaves, expansão de til, expansão de parâmetros e variáveis, substituição de comando, expansão aritmética, divisão de palavras e expansão de nome de caminho.
A ordem das expansões é: expansão de chaves; expansão de til, expansão de parâmetros e variáveis, expansão aritmética e substituição de comandos (feita da esquerda para a direita); divisão de palavras; e expansão do nome do caminho.
A parte importante para a sua situação é que a expansão das chaves ocorre antes da expansão da variável. Então, se você disse
grep -sHn "${searchstr}" "${line}"/{*,.*}
então
- a expansão da chave transformaria o último token em
"${line}"/*
e"${line}"/.*
, - expansão variável transformaria o acima em
/home/user/*
e/home/user/.*
, e então - a expansão do nome do caminho transformaria o acima em uma lista de nomes de arquivos.
Mas, quando você diz
grep -sHn "${searchstr}" ${grepdir}
então
- a expansão variável transforma o último token em
/home/user/{*,.*}
,
e então é tarde demais para ocorrer a expansão do aparelho.
grep
procura um arquivo chamado literalmente /home/user/{*,.*}
.
PS
grep -sHn "${searchstr}" "${line}/{*,.*}"
também não funcionaria, porque as aspas impediriam a ocorrência da expansão de chaves e do nome do caminho.
PPS Você não precisa de todos esses aparelhos;
grep -sHn "$searchstr" "$line"/{*,.*}
seria ótimo.
Responder2
A razão pela qual o grep é abortado ao recorrer em todo o sistema provavelmente não é porque ele não conseguiu lidar com a quantidade de dados, mas porque ele tropeça em um ou outro pseudo ou arquivo de dispositivo em/proc,/sys ou/dev. Você pode excluir os diretórios incorretos com a --exclude
opção na linha de comando.
A razão pela qual não expande os curingas é porque eles estão citados nesta linha:
grepdir="${line}/{*,.*}"
Mudá-lo para isso provavelmente ajudará na expansão.
grepdir="${line}/"{*,.*}
Outra maneira de conseguir isso (com menos scripts em seu nome) seria selecionar os arquivos usando find
e canalizando os caminhos dos arquivos xargs
para processamento:find / ... -print 0 | xargs -0 ...
No entanto, de qualquer forma, provavelmente ainda tropeçaria em qualquer arquivo (s) em que o grep recursivo original tropeçou, a menos que você os exclua.