Por que o loop for não é executado no diretório

Por que o loop for não é executado no diretório

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 findencontrar 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 forcomando 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', findprocuraremos 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 IFSvariável está definida como vazia, evitando assim reado corte de espaços iniciais e finais de uma linha. O readcomando usa uma string vazia ( -d '') como delimitador, para ler até atingir \0. findprecisa ser modificado de acordo, portanto, ele usa a -print0opçã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 ...; donedeveria ser usada para leitura findda saída também pode ser ruim. O whileloop 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 whileloop 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 whileloop 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é findterminar a execução e então pegue a saída e coloque-a de volta no forloop

for i in the output of find; do
    echo "2"
done

só então o loop for começará a ser executado.

Portanto, se findestiver demorando muito para terminar, o forloop terá que esperar muito tempo antes de poder começar.

Tente cronometrar o findcomando 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 forloop para repetir nomes de arquivos. Use um whileloop readassim:

find $DIRWORK -type d -name work | while read name; do
    echo "2"
done

LeressePara maiores informações.

Bônus: isso executa o whileloop em paralelo find. Isso significa que o whileloop executará uma iteração assim que findimprimir uma linha. Não é necessário esperar para findterminar a execução.

informação relacionada