¿Qué es la división de palabras? ¿Por qué es importante en la programación de shell?

16

Me estoy confundiendo sobre el papel que juega la división de palabras zsh. No he estado expuesto a este concepto al programar en C, Python o MATLAB, y esto ha despertado mi interés de por qué la división de palabras parece ser algo específico de la programación de shell.

He leído sobre la división de palabras en este y otros sitios antes, pero no he encontrado una explicación clara del concepto. Wikipedia tiene una definición de división de palabras, pero no parece tener referencias sobre cómo se aplica a los shells de Unix.

Aquí hay un ejemplo de mi confusión en zsh:

En las preguntas frecuentes de Z Shell , leo lo siguiente:

3.1: ¿Por qué $vardónde var="foo bar"no hace lo que espero?

En la mayoría de los derivados de Bourne-shell, las variables de varias palabras, como por ejemplo, var="foo bar" se dividen en palabras cuando se pasan a un comando o se usan en un for foo in $varbucle. Por defecto, zsh no tiene ese comportamiento: la variable permanece intacta. (¡Esto no es un error! Consulte a continuación). SH_WORD_SPLITExiste la opción de proporcionar compatibilidad.

Sin embargo, en el Manual de Z Shell , leí lo siguiente:

SH_WORD_SPLIT (-y) <K> <S>

Hace que la división de campo se realice en expansiones de parámetros sin comillas. Tenga en cuenta que esta opción no tiene nada que ver con la división de palabras. (Ver Expansión de parámetros).

¿Por qué dice que noSH_WORD_SPLIT tiene nada que ver con la división de palabras? ¿No es la división de palabras precisamente de lo que se trata todo esto?

Amelio Vazquez-Reina
fuente

Respuestas:

21

Los primeros shells tenían un solo tipo de datos: cadenas. Pero es común manipular listas de cadenas, generalmente cuando se pasan múltiples nombres de archivo como argumentos a un programa. Otro caso de uso común para la división es cuando un comando genera una lista de resultados: la salida del comando es una cadena, pero los datos deseados son una lista de cadenas. Para almacenar una lista de nombres de archivo en una variable, debe colocar espacios entre ellos. Entonces un script de shell como este

files="foo bar qux"
myprogram $files

llamado myprogramcon tres argumentos, ya que el shell dividió la cadena $filesen palabras. En ese momento, los espacios en los nombres de los archivos estaban prohibidos o se consideraban no realizados.

El shell Korn introdujo matrices: podría almacenar una lista de cadenas en una variable. El shell Korn seguía siendo compatible con el shell Bourne establecido en ese momento, por lo que las expansiones de variables desnudas se dividían en palabras, y el uso de matrices requería una sobrecarga sintáctica. Escribirías el fragmento de arriba

files=(foo bar qux)
myprogram "${files[@]}"

Zsh tenía matrices desde el principio, y su autor optó por un diseño de lenguaje más sano a expensas de la compatibilidad con versiones anteriores. En zsh (bajo las reglas de expansión predeterminadas) $varno realiza la división de palabras; si desea almacenar una lista de palabras en una variable, debe usar una matriz; y si realmente quieres dividir palabras, puedes escribir $=var.

files=(foo bar qux)
myprogram $files

En estos días, los espacios en los nombres de archivos son algo con lo que debe lidiar, tanto porque muchos usuarios esperan que funcionen como porque muchos scripts se ejecutan en contextos sensibles a la seguridad donde un atacante puede tener el control de los nombres de los archivos. Entonces, la división automática de palabras es a menudo una molestia; de ahí mi consejo general de usar siempre comillas dobles, es decir, escribir "$foo", a menos que comprenda por qué necesita dividir palabras en un caso de uso particular. (Tenga en cuenta que las expansiones variables desnudas también experimentan un problema).

Gilles 'SO- deja de ser malvado'
fuente
Gracias Gilles, ¡esto es realmente útil! ¿Es correcto decir que, en términos generales, la división de palabras convierte las cadenas del formulario "word1 word2 word3"en listas / matrices del formulario "word1" "word2" "word3"? También he actualizado el OP con una fuente específica de confusión en zsh.
Amelio Vazquez-Reina
1
@intrpc "División de palabras" no se divide en palabras del lenguaje natural sino en $IFScaracteres. Por lo tanto, "división de campo" es un nombre mejor. Pero "división de palabras" a menudo se usa para este concepto en la literatura de shell. La documentación de zsh está discutiendo sobre palabras.
Gilles 'SO- deja de ser malvado'
1
Vea también rc(el shell plan9, también portado a Unix) para un diseño aún mejor que zsh cuando se trata de variables y matrices.
Stéphane Chazelas
3

La división de palabras no es realmente específica de la shell.

La mayoría de los programas que necesitan analizar el ingreso de texto usan alguna forma de división de palabras como primer paso. Se realiza antes de identificar a partir de estas "palabras", números, operadores, cadenas, tokens y cualquier entidad similar que necesiten procesar.

Lo que es específico con los shells es que tienen que construir adecuadamente la lista de argumentos de los comandos llamados (C argc / argv, python sys.argv), incluyendo pasar argumentos con espacios incrustados, argumentos vacíos, delimitadores personalizados, etc. Muchos shells usan la variable IFS para permitir cierta flexibilidad allí.

jlliagre
fuente
3

En este caso específico de Zsh, la división de palabras se define de manera ligeramente diferente a la división de campos.

Considere prog a b c, pasará tres argumentos sin importar cómo lo configure IFS. Esta es la división de palabras .

Si lo hace A="a b c"; prog $A, pasará tres argumentos si IFSincluye espacio o un argumento de lo contrario. Esto es división de campo .

Las definiciones aquí son sutiles. Lo que el documento Zsh intenta decir es que, incluso si deshabilita esa opción, prog a b cseguirá obteniendo argumentos separados (que es lo que la gente siempre espera).

Hot.PxL
fuente
1
Bart Schaefer, un desarrollador zsh desde hace mucho tiempo, confirma que de hecho es el significado previsto de ese texto .
Stéphane Chazelas