
No script a seguir, o primeiroparaloop é executado conforme esperado, mas não o segundo. Não recebo nenhum erro, parece que o script simplesmente trava.
HOME=/root/mydir
DIR=$HOME/var
DIRWORK=$HOME/Local
for f in $(find $DIR -type f); do
lsof -n $f | grep [a-z] > /dev/null
if [ $? != 0 ]; then
echo "hi"
fi
done
for i in $(find $DIRWORK -type d -name work); do
echo "2"
done
Responder1
Seu script está codificado de maneira perigosa.
Primeiro, presumo que você esteja usando o shell Bash, já que o marcou como '/bash' e '/for'.
Na minha resposta vou citar este ótimoGuia do Bash, que é provavelmente a melhor fonte para aprender Bash.
1)Nunca use umSubstituição de comando, dequalquergentil, sem aspas. Há um grande problema aqui: usar uma expansão sem aspas para dividir a saída em argumentos.
Especificamente falando, isso $(find $DIRWORK -type d -name work)
e $(find $DIR -type f)
passará porDivisão de palavras, portanto, se find
encontrar um arquivo com espaços em seu nome, ou seja, "nome do arquivo", o resultado da divisão de palavras do Bash passará 2 argumentos para o for
comando iterar, ou seja, um para "arquivo" e outro para "nome". Nesse caso, você deseja obter um "arquivo: arquivo ou diretório inexistente" e "nome: arquivo ou diretório inexistente", em vez de potencialmente causar danos a eles, se eles realmente existirem.
2)Por convenção, variáveis de ambiente (PATH, EDITOR, SHELL, ...) e variáveis internas do shell (BASH_VERSION, RANDOM, ...) são totalmente maiúsculas. Todos os outros nomes de variáveis devem estar em letras minúsculas. Como os nomes das variáveis diferenciam maiúsculas de minúsculas, esta convenção evita a substituição acidental de variáveis ambientais e internas.
Seu diretório $DIRWORK quebra essa convenção e também não está entre aspas, portanto, se deixarmos DIRWORK='/path/to/dir1 /path/to/dir2'
, find
procuraremos dois diretórios diferentes quando $DIRWORK não estiver entre aspas. O assunto do uso de aspas é muito importante no Bash, então você deve "aspas duplas"todoexpansão, bem como qualquer coisa que possa conter um caractere especial, por exemplo, "$var", "$@", "${array[@]}", "$(command)". Bash trata tudo entre 'aspas simples' como literal. Aprenda a diferença entre ' e " e `. VejaCitações,Argumentose você também pode querer dar uma olhada neste link:http://wiki.bash-hackers.org/syntax/words
Esta é uma versão mais segura do seu script, que recomendo que você use:
my_home="/root/mydir"
my_dir="$my_home/var"
dir_work="$my_home/Local"
while IFS= read -r -d '' f; do
# I'm guessing that you also want to ignore stderr;
# this is where the 2>&1 came from.
if lsof -n "$f" | grep '[a-z]' > /dev/null 2>&1; then
echo "hey, I'm safer now!"
fi
done < <(find "$dir_work" -type f -print0)
while IFS= read -r -d '' f; do
echo "2"
done < <(find "$dir_work" -type d -name 'work' -print0)
Como você pode ver, a IFS
variável está definida como vazia, evitando assim read
o corte de espaços iniciais e finais de uma linha. O read
comando usa uma string vazia ( -d ''
) como delimitador, para ler até atingir \0.
find
precisa ser modificado de acordo, portanto, ele usa a -print0
opção de delimitar seus dados com \0 em vez de uma nova linha - que, surpreendente e maliciosamente, pode fazer parte de um nome de arquivo. Dividir esse arquivo por \n em duas partes quebrará nosso código.
Você pode querer ler sobreSubstituição de Processose você não entende meu roteiro completamente.
A resposta anterior que afirmava que find ... | while read name; do ...; done
deveria ser usada para leitura find
da saída também pode ser ruim. O while
loop acima é executado em um novo subshell com sua própria cópia das variáveis copiadas do pai. Esta cópia é usada para o que você quiser. Quando o while
loop termina, a cópia do subshell é descartada e as variáveis originais do pai não são alteradas.
Se você pretende modificar algumas variáveis dentro deste while
loop e usá-las posteriormente no pai, considere usar o script mais seguro acima, que evitará a perda de dados.
Responder2
Este código
for i in $(find $DIRWORK -type d -name work); do
echo "2"
done
irá primeiro executar esta linha
find $DIRWORK -type d -name work
espere até find
terminar a execução e então pegue a saída e coloque-a de volta no for
loop
for i in the output of find; do
echo "2"
done
só então o loop for começará a ser executado.
Portanto, se find
estiver demorando muito para terminar, o for
loop terá que esperar muito tempo antes de poder começar.
Tente cronometrar o find
comando em um prompt interativo
$ time find $DIRWORK -type d -name work
e veja quanto tempo isso leva.
Observe também: você não deve usar um for
loop para repetir nomes de arquivos. Use um while
loop read
assim:
find $DIRWORK -type d -name work | while read name; do
echo "2"
done
LeressePara maiores informações.
Bônus: isso executa o while
loop em paralelo find
. Isso significa que o while
loop executará uma iteração assim que find
imprimir uma linha. Não é necessário esperar para find
terminar a execução.