¿Por qué no se ejecuta el bucle for en el directorio?

¿Por qué no se ejecuta el bucle for en el directorio?

En el siguiente guión, el primeroparaEl bucle se ejecuta como se esperaba, pero no el segundo. No recibo ningún error, parece que el script simplemente se cuelga.

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

Respuesta1

Su secuencia de comandos está codificada de forma peligrosa.

Primero, supongo que está utilizando el shell Bash ya que lo etiquetó como '/bash' y '/for'.

En mi respuesta citaré este granguía de fiesta, que es probablemente la mejor fuente para aprender Bash que existe.

1)Nunca utilices unSustitución de comando, decualquieraamable, sin comillas. Aquí hay un problema importante: usar una expansión sin comillas para dividir el resultado en argumentos.

En concreto, esto $(find $DIRWORK -type d -name work)y $(find $DIR -type f) se someteráDivisión de palabras, por lo tanto, si findencuentra un archivo con espacios en su nombre, es decir, "nombre de archivo", el resultado de la división de palabras de Bash pasará 2 argumentos para que el forcomando itere, es decir, uno para "archivo" y otro para "nombre". En este caso, desea obtener un "archivo: No existe tal archivo o directorio" y "nombre: No existe tal archivo o directorio", en lugar de causarles daños potenciales si realmente existen.

2)Por convención, las variables de entorno (PATH, EDITOR, SHELL, ...) y las variables internas del shell (BASH_VERSION, RANDOM, ...) están completamente en mayúscula. Todos los demás nombres de variables deben estar en minúsculas. Dado que los nombres de las variables distinguen entre mayúsculas y minúsculas, esta convención evita anular accidentalmente las variables ambientales e internas.

Su directorio $DIRWORK rompe esa convención y tampoco está entre comillas, por lo tanto, si lo permitimos DIRWORK='/path/to/dir1 /path/to/dir2', findbuscará en dos directorios diferentes cuando $DIRWORK no esté entre comillas. El tema del uso de comillas es muy importante en Bash, por lo que debes hacer "comillas dobles".cadaexpansión, así como cualquier cosa que pueda contener un carácter especial, por ejemplo, "$var", "$@", "${array[@]}", "$(command)". Bash trata todo lo que está dentro de "comillas simples" como literal. Conozca la diferencia entre 'y "y `. ConsulteCitas,Argumentosy quizás también quieras echar un vistazo a este enlace:http://wiki.bash-hackers.org/syntax/words

Esta es una versión más segura de tu script, que te recomiendo que uses en su lugar:

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 puede ver, la IFSvariable está configurada para estar vacía, lo que impide readrecortar los espacios iniciales y finales de una línea. El readcomando utiliza una cadena vacía ( -d '') como delimitador, para leer hasta llegar a \0. finddebe modificarse en consecuencia, por lo que utiliza la -print0opción de delimitar sus datos con \0 en lugar de una nueva línea, que, sorprendente y maliciosamente, puede ser parte de un nombre de archivo. Dividir dicho archivo mediante \n en dos partes romperá nuestro código.

Quizás quieras leer sobreSustitución de procesossi no entiendes mi guión completamente.

La respuesta anterior que decía que find ... | while read name; do ...; donedebería usarse para leer findla salida también puede ser mala. El whilebucle anterior se ejecuta en una nueva subcapa con su propia copia de variables copiadas del padre. Esta copia luego se usa para lo que quieras. Cuando whilefinaliza el ciclo, la copia del subshell se descarta y las variables originales del padre no cambiaron.

Si su objetivo es modificar algunas variables dentro de este whilebucle y usarlas luego en el padre, considere usar el script más seguro anterior que evitará la pérdida de datos.

Respuesta2

este codigo

for i in $(find $DIRWORK -type d -name work); do
    echo "2"
done

primero ejecutará esta línea

find $DIRWORK -type d -name work

espere hasta que findtermine de ejecutarse y luego tome la salida y vuelva a colocarla en el forbucle

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

sólo entonces el bucle for comenzará a ejecutarse.

Entonces, si findtarda mucho en finalizar, el forciclo debe esperar mucho tiempo antes de poder comenzar.

Intente cronometrar el findcomando en un mensaje interactivo

$ time find $DIRWORK -type d -name work

y vea cuánto tiempo lleva.


También tenga en cuenta: no debe utilizar un forbucle para recorrer los nombres de archivos. Utilice un whilebucle readcomo este:

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

Leerestepara más información.

Bonificación: esto ejecuta el whilebucle en paralelo a find. Eso significa que el whilebucle ejecutará una iteración tan pronto como findimprima una línea. No tiene que esperar a findque termine de ejecutarse.

información relacionada