Bash Scripting y cita

1

Estoy leyendo un par de libros sobre scripts de bash y me cuesta entender las citas adecuadas y el uso de IFS. Quizás alguien pueda ayudarme con un pequeño ejemplo que involucra nombres de archivo con comillas. Al hacer esto desde una línea de comando, esto funciona para imprimir los nombres de archivo 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é fallan todos estos?

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 archivo no se dividen en absoluto (los forbucles solo 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 anterior). Si es así, ¿por qué aparentemente necesito en $'\n'lugar de solo \n?

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

Diagon
fuente
Bash es la versión 4.3.42 (1) en Ubuntu Gnome 16.04. No puede imprimir los nombres de archivo correctamente. Los rompe en espacios cuando debería romperse en líneas nuevas. Traté de IFS=$'\n' set - encontrar. -name "*" -print`` y for i in "$@"; do echo $i; done. Definitivamente no funciona.
Diagon
Ah, ya veo. Tienes un ;después del IFS=... yo no. Es como el segundo caso en el segundo grupo de 3. La pregunta es, ¿por qué es necesario? Espero que sin el ;, la modificación de la variable sea temporal, dentro del siguiente comando. No quiero que sea permanente.
Diagon
En responder a por qué IFS=$'\n'; set - `find`;trabajar pero no IFS=$'\n' set - `find`;. Una explicación plausible es que en IFS=$'\n' set - `find`;, findse sustituye y se tokeniza con IFS actual antes de setejecutarse.
guest-vm

Respuestas:

0

Dos cosas que aprendí al depurar comandos bash: una) Si no funciona como cree que debería, repita los comandos para asegurarse de que se muestren de la manera que usted cree que deberían hacerlo. 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 al pie de la letra, por lo que deberás explicar lo que quieres que haga. ¿Tiene espacios en sus nombres de archivo que están causando problemas con su comando? En tanto que a deshacerse del espacio de la variable IFS, es probable que funcione de la manera deseada: IFS=$'\n\t'. La -print0opción básicamente elimina todos los separadores de línea, por lo que todo lo que desee de esos comandos no tiene separadores a menos que los archivos tengan espacios en sus nombres. Para su segunda serie 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 a: $'\n' for i in. Después de eso, la siguiente línea se lee como:, do echo $ique no funciona ya que el docomando requiere un ciclo de algún tipo para ser el comando anterior. El siguiente funciona porque el punto y coma le dice a bash que se ha alcanzado el 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 anterior). Si es así, ¿por qué aparentemente necesito $ '\ n' en lugar de solo \ n?

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

Blerg
fuente
Sí, hay espacios en los nombres de archivo, y ese es el problema (aclaré anteriormente). (1) Eliminar los espacios en el IFS no funciona set. Estoy seguro. (2) Comprendí que var=value <command>debería establecerse vartemporalmente en el valor cuando se ejecuta el comando. Aquí el comando es un for, ¿no? (3) El último comando tiene un ;después del IFS=, pero todavía no funciona aunque -print0debería producir nombres de archivo separados por nulos.
Diagon
Sí, el -print0comando debe agregar caracteres nulos en lugar de líneas nuevas; sin embargo, bash los elimina automáticamente. Deberá guardar la salida en un archivo si desea conservarlos; Sin embargo, no estoy seguro de que esto sea lo que querrá, ya que Bash eliminará los caracteres nulos si intenta usarlos. Bash no permitirá caracteres nulos en las variables, como puede ver aquí .
Blerg
Eso es extraño. También parece significar que IFS=''no funciona, porque no puedo configurar IFS en\0
Diagon
Entonces, en ese caso, en los ejemplos que usan set, lo único que me deja perplejo es que IFS=$'\n' set - `find . -name "*" -print`falla, rompiendo los nombres de los archivos en los espacios.
Diagon
Establecer <code> IFS = '' </code> es lo mismo que borrarlo. Dado que bash ignora los caracteres nulos, no hay razón para verificarlos; lo que significa que no hay separación de argumento adicional en absoluto. Puede poner lo que quiera en <code> IFS </code> y bash lo interpretará como un salto de argumento adicional (caracteres, no cadenas). En cuanto a su segundo comentario, <code> IFS </code> no se configura de la manera que desea hasta que finaliza el comando. Ponga un punto y coma entre <code> IFS = $ '\ n' </code> y <code> set - find . -name "*" -print</code> y eso debería solucionar su problema.
Blerg