valor strings sort script por ordenar example campo array arrays bash bash-completion

arrays - strings - Una confusión sobre ${array[*]} versus ${array[@]} en el contexto de una finalización de Bash



ordenar array php por campo (2)

Estoy probando cómo escribir una finalización de Bash por primera vez, y estoy un poco confundido acerca de las dos formas de desreferenciar las matrices de Bash ( ${array[@]} y ${array[*]} ).

Aquí está la parte relevante del código (funciona, por cierto, pero me gustaría entenderlo mejor):

_switch() { local cur perls local ROOT=${PERLBREW_ROOT:-$HOME/perl5/perlbrew} COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} perls=($ROOT/perls/perl-*) # remove all but the final part of the name perls=(${perls[*]##*/}) COMPREPLY=( $( compgen -W "${perls[*]} /usr/bin/perl" -- ${cur} ) ) }

La documentación de Bash dice :

Se puede hacer referencia a cualquier elemento de una matriz utilizando $ {nombre [subíndice]}. Las llaves son necesarias para evitar conflictos con los operadores de expansión del nombre de archivo del shell. Si el subíndice es ''@'' o ''*'', la palabra se expande a todos los miembros del nombre de la matriz. Estos subíndices difieren solo cuando la palabra aparece entre comillas dobles. Si la palabra está entre comillas dobles, $ {nombre [*]} se expande a una sola palabra con el valor de cada miembro del arreglo separado por el primer carácter de la variable IFS, y $ {nombre [@]} expande cada elemento del nombre a una palabra separada.

Ahora creo que comprendo que compgen -W espera una cadena que contenga una lista de palabras con posibles alternativas, pero en este contexto no entiendo lo que significa "$ {nombre [@]} expande cada elemento del nombre a una palabra separada".

Larga historia corta: ${array[*]} funciona; ${array[@]} no lo hace. Me gustaría saber por qué, y me gustaría entender mejor en qué se expande exactamente ${array[@]} .


(Esta es una expansión de mi comentario sobre la respuesta de Kaleb Pederson: vea esa respuesta para un tratamiento más general de [@] vs [*] .)

Cuando bash (o cualquier shell similar) analiza una línea de comando, la divide en una serie de "palabras" (que llamaré "shell-words" para evitar confusiones más adelante). En general, las palabras de shell están separadas por espacios (u otros espacios en blanco), pero los espacios se pueden incluir en una palabra de shell escapando o citándolos. La diferencia entre las matrices expandidas [@] y [*] comillas dobles es que "${myarray[@]}" que cada elemento de la matriz se trate como una palabra shell diferente, mientras que "${myarray[*]}" da como resultado una sola palabra de shell con todos los elementos de la matriz separados por espacios (o cualquiera que sea el primer carácter de IFS ).

Por lo general, el comportamiento [@] es lo que quieres. Supongamos que tenemos perls=(perl-one perl-two) y usamos ls "${perls[*]}" , que es equivalente a ls "perl-one perl-two" , que buscará un único archivo llamado perl-one perl-two , que probablemente no es lo que querías. ls "${perls[@]}" es equivalente a ls "perl-one" "perl-two" , que es mucho más probable que haga algo útil.

Proporcionar una lista de palabras para completar (que llamaré comp-words para evitar confusiones con shell-words) para compgen es diferente; La opción -W toma una lista de palabras comp, pero debe tener la forma de una sola palabra de shell con las palabras comp separadas por espacios. Tenga en cuenta que las opciones de comando que toman argumentos siempre (por lo menos que yo sepa) toman una sola palabra de shell; de lo contrario, no habría manera de saber cuándo terminan los argumentos de la opción y los argumentos del comando regular (/ otros banderas de opción) comienzan.

Con más detalle:

perls=(perl-one perl-two) compgen -W "${perls[*]} /usr/bin/perl" -- ${cur}

es equivalente a:

compgen -W "perl-one perl-two /usr/bin/perl" -- ${cur}

... que hace lo que tu quieres. Por otra parte,

perls=(perl-one perl-two) compgen -W "${perls[@]} /usr/bin/perl" -- ${cur}

es equivalente a:

compgen -W "perl-one" "perl-two /usr/bin/perl" -- ${cur}

... que es un completo disparate: "perl-one" es la única palabra de compilación adjunta al indicador -W, y el primer argumento real, que compgen tomará como la cadena que se completará, es "perl-two / usr / bin / perl ". Espero que Compgen se queje de que se le han dado argumentos adicionales ("-" y lo que sea en $ cur), pero aparentemente los ignora.


Su título pregunta sobre ${array[@]} comparación con ${array[*]} pero luego pregunta por $array[*] en lugar de $array[@] cual es un poco confuso. Voy a responder a las dos:

Cuando cita una variable de matriz y usa @ como un subíndice, cada elemento de la matriz se expande a su contenido completo independientemente del espacio en blanco (en realidad, uno de $IFS ) que pueda estar presente dentro de ese contenido. Cuando usa el asterisco ( * ) como subíndice (independientemente de si está citado o no), puede expandirse al nuevo contenido creado al dividir el contenido de cada elemento de la matriz en $IFS .

Aquí está el script de ejemplo:

#!/bin/sh myarray[0]="one" myarray[1]="two" myarray[3]="three four" echo "with quotes around myarray[*]" for x in "${myarray[*]}"; do echo "ARG[*]: ''$x''" done echo "with quotes around myarray[@]" for x in "${myarray[@]}"; do echo "ARG[@]: ''$x''" done echo "without quotes around myarray[*]" for x in ${myarray[*]}; do echo "ARG[*]: ''$x''" done echo "without quotes around myarray[@]" for x in ${myarray[@]}; do echo "ARG[@]: ''$x''" done

Y aquí está la salida:

with quotes around myarray[*] ARG[*]: ''one two three four'' with quotes around myarray[@] ARG[@]: ''one'' ARG[@]: ''two'' ARG[@]: ''three four'' without quotes around myarray[*] ARG[*]: ''one'' ARG[*]: ''two'' ARG[*]: ''three'' ARG[*]: ''four'' without quotes around myarray[@] ARG[@]: ''one'' ARG[@]: ''two'' ARG[@]: ''three'' ARG[@]: ''four''

Personalmente, normalmente quiero "${myarray[@]}" . Ahora, para responder a la segunda parte de su pregunta, ${array[@]} versus $array[@] .

Citando los documentos de bash, que citó:

Las llaves son necesarias para evitar conflictos con los operadores de expansión del nombre de archivo del shell.

$ myarray= $ myarray[0]="one" $ myarray[1]="two" $ echo ${myarray[@]} one two

Pero, cuando haces $myarray[@] , el signo de dólar está estrechamente vinculado a myarray por lo que se evalúa antes de [@] . Por ejemplo:

$ ls $myarray[@] ls: cannot access one[@]: No such file or directory

Pero, como se indica en la documentación, los paréntesis son para la expansión del nombre de archivo, así que intentemos esto:

$ touch one@ $ ls $myarray[@] one@

Ahora podemos ver que la expansión del nombre de archivo ocurrió después de la $myarray myarray.

Y una nota más, $myarray sin un subíndice se expande al primer valor de la matriz:

$ myarray[0]="one four" $ echo $myarray[5] one four[5]