¿Cuál es la estructura de datos de $ @ en shell?

13

Usualmente $@representamos todos los argumentos excepto $ 0. Sin embargo, no sé qué $@es la estructura de datos .

¿Por qué se comporta de manera diferente $*cuando se incluye entre comillas dobles, alguien podría darme una explicación a nivel de intérprete?

Se puede iterar en el bucle for, por lo que parece ser una matriz. Sin embargo, también puede repetirse completamente con simple echo $@, si es una matriz, solo se mostrará el primer elemento. Debido a la limitación de shell, no puedo escribir más código de experimento para llevarlo a cabo.

Diferencia entre esta publicación : Esta publicación muestra cómo se $@comporta de manera diferente $*. Pero me pregunto sobre el tipo de datos de $@. Shell como lenguaje de interpretación, como Python, debe representar los datos de acuerdo con una serie de tipos fundamentales. O en otras palabras, quiero saber cómo $ @ se almacena en la memoria de la computadora.

¿Es una cadena, una cadena de varias líneas o una matriz?

Si es un tipo de datos único, ¿es posible definir una variable personalizada como una instancia de este tipo?

davmos
fuente
1
Posible duplicado de ¿Cuál es la diferencia entre $ * y $ @?
Haxiel
@Haxiel, no lo creo, escribí su diferencia al final de mi publicación.
davmos
Sería mejor para usted probar la diferencia de salida con printf '%s\n' "$@"y printf '%s\n' "$*". La echoutilidad solo genera sus argumentos, sin importar si son uno o varios. Ambas son matrices (de cadenas), pero se comportan de manera diferente cuando se citan dos veces. Si cualquiera era una cadena de varias líneas, entonces no podrían almacenar cadenas de varias líneas (que pueden). No está claro qué problema está tratando de resolver.
Kusalananda
2
Su pregunta es el equivalente a preguntar qué es una @varvariable en Perl, en términos de su almacenamiento subyacente. Desde el punto de vista de un programa Perl ordinario, realmente no importa, aparte de eso, es accesible como una matriz / lista (y el hecho de que hay contextos en los que se espera una lista).
Kusalananda

Respuestas:

16

Eso comenzó como un truco en el shell Bourne. En el shell Bourne, la división de palabras IFS se realizó (después de la tokenización) en todas las palabras en el contexto de la lista (argumentos de línea de comando o las palabras en las que se forrepiten los bucles). Si tuvieras:

IFS=i var=file2.txt
edit file.txt $var

Esa segunda línea se tokenised en 3 palabras, $varse ampliaría, y dividir + glob se llevaría a cabo en las tres palabras, por lo que terminaría corriendo edcon t, f, le.txt, f,le2.txt como argumentos.

Citar partes de eso evitaría la división + glob. El shell Bourne inicialmente recordó qué caracteres fueron citados al establecer el octavo bit en ellos internamente (eso cambió más tarde cuando Unix se limpió a 8 bits, pero el shell aún hizo algo similar para recordar qué byte fue citado).

Ambos $*y $@fueron la concatenación de los parámetros posicionales con espacio intermedio. Pero hubo un procesamiento especial de $@cuándo dentro de comillas dobles. Si está $1contenido foo bary $2contenido baz, "$@"se expandiría a:

foo bar baz
^^^^^^^ ^^^

(con la ^s anterior que indica cuáles de los caracteres tienen el octavo bit establecido). Donde se citó el primer espacio (tenía el octavo bit establecido) pero no el segundo (el agregado entre palabras).

Y es la división IFS la que se encarga de separar los argumentos (suponiendo que el carácter de espacio esté $IFScomo está por defecto). Es similar a cómo $*se expandió en su predecesor el shell Mashey (basado en el shell Thomson, mientras que el shell Bourne se escribió desde cero).

Eso explica por qué en el shell Bourne inicialmente "$@"se expandiría a la cadena vacía en lugar de nada en absoluto cuando la lista de parámetros posicionales estaba vacía (tenía que evitarlo ${1+"$@"}), por qué no mantuvo los parámetros posicionales vacíos y por qué "$@"no no funciona cuando$IFS no contiene el carácter de espacio.

La intención era poder pasar la lista de argumentos literalmente a otro comando, pero eso no funcionó correctamente para la lista vacía, para elementos vacíos o cuando $IFSno contenía espacio (los dos primeros problemas finalmente se solucionaron en versiones posteriores )

El shell Korn (en el que se basa la especificación POSIX) cambió ese comportamiento de varias maneras:

  • La división IFS solo se realiza en el resultado de expansiones sin comillas (no en palabras literales como editofile.txt en el ejemplo anterior)
  • $*y $@se unen con el primer carácter de $IFSo espacio cuando $IFSestá vacío, excepto que para una cita "$@", esa unión no se cita como en el shell Bourne, y para una cita "$*"cuando IFSestá vacía, los parámetros posicionales se agregan sin separador.
  • se añadió soporte para las matrices, y con ${array[@]} ${array[*]}reminiscencia de Bourne de $*y $@pero a partir de indice 0 en lugar de 1, y escasa (más como matrices asociativas) lo que significa $@realmente no pueden ser tratados como una matriz de ksh (comparar con csh/ rc/ zsh/ fish/ yashdonde $argv/ $*son normales matrices).
  • Los elementos vacíos se conservan.
  • "$@"cuando $#es 0 ahora se expande a nada en lugar de la cadena vacía, "$@"funciona cuando $IFSno contiene espacios, excepto cuando IFSestá vacío. Un $*sin comillas sin comodines se expande a un argumento (donde los parámetros posicionales se unen con el espacio) cuando $IFSestá vacío.

ksh93 solucionó los pocos problemas restantes anteriores. En ksh93, $*y se $@expande a la lista de parámetros posicionales, separados independientemente del valor de $IFS, y luego se divide + globbed + brace-expandido en contextos de lista, $*unido con el primer byte (no carácter) de $IFS, "$@"en contextos de lista se expande a la lista de parámetros posicionales, independientemente del valor de $IFS. En un contexto que no es de lista, como en var=$@, $@se une con espacio independientemente del valor de $IFS.

bashLas matrices están diseñadas después de las ksh. Las diferencias son:

  • sin llave de expansión en la expansión sin comillas
  • primer carácter de en $IFSlugar de por byte
  • algunas diferencias de mayúsculas y minúsculas, como la expansión de $*cuando no se cita en un contexto sin lista cuando $IFSestá vacío.

Si bien la especificación POSIX solía ser bastante vaga, ahora más o menos especifica el comportamiento bash.

Es diferente de las matrices normales en ksho bashen eso:

  • Los índices comienzan en 1 en lugar de 0 (excepto en el "${@:0}"que incluye $0(no es un parámetro posicional, y en funciones le da el nombre de la función o no dependiendo del shell y cómo se definió la función)).
  • No puedes asignar elementos individualmente
  • no es escaso, no puedes desarmar elementos individualmente
  • shift puede ser usado.

En zsho yashdonde las matrices son matrices normales (no dispersas, los índices comienzan en uno como en todos los demás shells pero ksh / bash), $*se trata como una matriz normal. zshtiene $argvcomo un alias para ello (por compatibilidad con csh). $*es lo mismo que $argvor ${argv[*]}(argumentos unidos con el primer carácter de $IFSpero aún separados en contextos de lista). Me "$@"gusta "${argv[@]}"o se "${*[@]}"}somete al procesamiento especial de estilo Korn.

Stéphane Chazelas
fuente
8

Sin embargo, no sé qué $@es la estructura de datos .

Es un parámetro especial que se expande a los valores de los parámetros posicionales ... Pero eso no es nada específico sobre la terminología.

Podemos ver los parámetros posicionales como partes de $@, por lo que tiene una serie de elementos distintos ( $1, $2...), a los que se puede acceder de forma independiente y se nombran por números naturales consecutivos. Eso lo convierte en algo que generalmente se llama una matriz.

Sin embargo, la sintaxis es un poco extraña e incluso limitada. No hay forma de modificar un solo elemento de la matriz individualmente. En cambio, todo se debe configurar de una vez. (Puede usar set -- "$@" foopara agregar un valor o set -- "${@:1:2}" foo "${@:3}"para agregar un valor en el medio. Pero en ambos casos debe escribir toda la lista resultante).

Por qué se comporta de manera diferente $*cuando se incluye entre comillas dobles,

Porque están definidos para comportarse de manera diferente.

Sin embargo, también puede repetirse completamente con simple echo $@, si es una matriz, solo se mostrará el primer elemento.

Si te refieres al hecho de que solo a=(foo bar asdf); echo $adará salida foo, entonces esto es principalmente una peculiaridad de la sintaxis de shell, y el hecho de que las matrices con nombre de estilo ksh se crearon más tarde que los parámetros de posición y $@. Plain $aes el mismo ${a[0]}que tiene el significado compatible con versiones anteriores de un solo valor escalar, independientemente de si aes una matriz o una variable escalar simple.

El @signo que se refiere a la lista completa se reutilizó con matrices con nombre, ya que "${a[@]}"es la forma de obtener la lista completa. En comparación con las matrices con $@nombre, solo se omiten las llaves y los corchetes innecesarios y el nombre.

O en otras palabras, quiero saber cómo se $@almacena en la memoria de la computadora.

Eso depende de la implementación, tendrá que buscar el código fuente de cualquier shell particular que le interese.

¿Es una cadena, una cadena de varias líneas o una matriz?

Una matriz, en su mayoría. Aunque son diferentes de las matrices con nombre de estilo ksh, ya que pueden tener enteros arbitrarios no negativos como índices, no solo los consecutivos como con $@. (Es decir, una matriz denominada puede ser escasa, y tienen por ejemplo los índices 1, 3y 4, con 0y 2que falta. Eso no es posible con los parámetros posicionales.)

No es una sola cadena, ya que se puede expandir a elementos distintos, y llamar a las líneas de elementos tampoco es correcto, ya que cualquier variable regular o uno de los parámetros posicionales (elementos de $@) también pueden contener nuevas líneas.

Si es un tipo de datos único, ¿es posible definir una variable personalizada como una instancia de este tipo?

No. Pero las matrices con nombre son probablemente más útiles de todos modos.

ilkkachu
fuente
1
+1. TL: DR $@no es una estructura de datos, es una de las pocas funciones / operadores para expandir la estructura de datos de parámetros posicionales.
Peter Cordes