
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 find
encuentra 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 for
comando 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'
, find
buscará 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 IFS
variable está configurada para estar vacía, lo que impide read
recortar los espacios iniciales y finales de una línea. El read
comando utiliza una cadena vacía ( -d ''
) como delimitador, para leer hasta llegar a \0.
find
debe modificarse en consecuencia, por lo que utiliza la -print0
opció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 ...; done
debería usarse para leer find
la salida también puede ser mala. El while
bucle 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 while
finaliza 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 while
bucle 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 find
termine de ejecutarse y luego tome la salida y vuelva a colocarla en el for
bucle
for i in the output of find; do
echo "2"
done
sólo entonces el bucle for comenzará a ejecutarse.
Entonces, si find
tarda mucho en finalizar, el for
ciclo debe esperar mucho tiempo antes de poder comenzar.
Intente cronometrar el find
comando 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 for
bucle para recorrer los nombres de archivos. Utilice un while
bucle read
como este:
find $DIRWORK -type d -name work | while read name; do
echo "2"
done
Leerestepara más información.
Bonificación: esto ejecuta el while
bucle en paralelo a find
. Eso significa que el while
bucle ejecutará una iteración tan pronto como find
imprima una línea. No tiene que esperar a find
que termine de ejecutarse.