Bash Scripting y cotización

Bash Scripting y cotización

Estoy leyendo un par de libros sobre secuencias de comandos bash y me cuesta entender las citas adecuadas y el uso de IFS. Quizás alguien pueda ayudarme con un pequeño ejemplo que incluya nombres de archivos entre comillas. Al hacer esto desde una línea de comando, esto funciona para imprimir los nombres de archivos correctamente, incluso si incluyen espacios:

set - *
for i in "$@"; do echo $i; done

Esto no funciona, ya que se rompe en los espacios:

set - `find . -name "*"`
for i in "$@"; do echo $i; done

Ni tampoco:

IFS=$'\0' set - `find . -name "*" -print0`
for i in "$@"; do echo $i; done

Tampoco la combinación que usa IFS=$'\n'y -print. ¿Por qué todos estos fallan?

Lo siguiente también falla, pero en este caso se produce un error ("bash: error de sintaxis cerca del token inesperado `do'"). ¿Por qué?

IFS=$'\n' for i in `find . -name "*" -type f`; do echo $i; done 

pero esto funciona (observe el ";"):

IFS=$'\n'; for i in `find . -name "*" -type f`; do echo $i; done 

y esto falla porque los nombres de los archivos no están divididos en absoluto (los forbucles solo se repiten una vez):

IFS=''; for i in `find . -name "*" -type f -print0`; do echo -e "$i\n"; done

Entonces, de nuevo, ¿por qué fallan el primero y el tercero?

Finalmente, ¿estoy en lo cierto al creer que al configurar IFS, ''es lo mismo que $'\0'? (Probé ambos en el ejemplo inmediatamente anterior). Si es así, ¿por qué aparentemente necesito $'\n'en lugar de solo \n?

*Bash es la versión 4.3.42(1) en Ubuntu Gnome 16.04.

Respuesta1

Dos cosas que aprendí al depurar comandos bash: Una) Si no funciona como cree que debería, haga eco/imprima los comandos para asegurarse de que se muestren como cree que deberían. Dos) Ejecute manualmente los comandos que tiene dentro de sus comandos para ver si se ejecutan correctamente (a menos que sean comandos potencialmente destructivos, como: rm, dd, chmod 777, etc.).

¿Cómo o qué estás tratando de lograr exactamente? El mío parece funcionar bien con ese comando palabra por palabra, por lo que deberás explicar qué quieres que haga. ¿Tiene espacios en sus nombres de archivos que están causando problemas con su comando? Siempre que elimine el espacio de la variable IFS, probablemente funcionará de la manera deseada: IFS=$'\n\t'. Básicamente, la -print0opción elimina todos los separadores de línea, por lo que todo lo que desee de esos comandos no tiene ningún separador a menos que los archivos tengan espacios en sus nombres. Para su segundo conjunto de preguntas:

IFS=$'\n' for i in `find . -name "*" -type f`; do echo $i; done 

Este comando no se ejecuta correctamente porque bash lo lee así:

IFS=$'\n' for i in `find . -name "*" -type f`
do echo $i
done

La primera línea establece IFS en: $'\n' for i in. Después de eso, la siguiente línea dice: do echo $i, lo cual no funciona ya que el docomando requiere un bucle de algún tipo para ser el comando anterior. El siguiente funciona porque el punto y coma le dice a bash que se ha llegado al final del comando, por lo que:

IFS=$'\n'
for i in `find . -name "*" -type f`
do echo $i
done 

Finalmente, ¿estoy en lo cierto al creer que al configurar IFS, '' es lo mismo que $'\0'? (Probé ambos en el ejemplo inmediatamente anterior). Si es así, ¿por qué aparentemente necesito $'\n' en lugar de solo \n?

Sí a la primera parte, ''y '\0'ambas se consideran NULL, por lo que son básicamente iguales. Esto se debe a que bash se interpreta $''como una cadena ANSI C. Y necesita tener comillas alrededor de \n para que bash lo interprete como una cadena C de modo que se coloque un carácter de nueva línea en el IFS.
Puedes leer más sobre estosaquí.

información relacionada