Quelle est la structure de données de $ @ dans le shell?

13

Nous utilisons habituellement $@pour représenter tous les arguments sauf $ 0. Cependant, je ne sais pas ce qu'est la structure des données $@.

Pourquoi cela se comporte-t-il différemment avec l' $*inclusion de guillemets doubles, est-ce que quelqu'un pourrait me donner une explication de niveau interprète?

Il peut être itéré dans la boucle for, il semble donc être un tableau. Cependant, il peut également résonner entièrement avec simple echo $@, s'il s'agit d'un tableau, seul le premier élément sera affiché. En raison de la limitation du shell, je ne peux pas écrire plus de code d'expérience pour le réaliser.

Différence entre ce post : Ce post montre comment $@se comporte différemment de $*. Mais je me pose des questions sur le type de données de $@. Shell en tant que langage d'interprétation, comme Python, devrait représenter les données selon une série de types fondamentaux. Ou en d'autres termes, je veux savoir comment $ @ est stocké dans la mémoire de l'ordinateur.

Est-ce une chaîne, une chaîne multi-lignes ou un tableau?

S'il s'agit d'un type de données unique, est-il possible de définir une variable personnalisée comme instance de ce type?

davmos
la source
@Haxiel, je ne pense pas, j'ai écrit leur différence au bas de mon post.
davmos
Vous seriez mieux servi en testant la différence de sortie avec printf '%s\n' "$@"et printf '%s\n' "$*". L' echoutilitaire sort simplement ses arguments, qu'ils soient un ou plusieurs. Les deux sont des tableaux (de chaînes), mais ils se comportent différemment lorsqu'ils sont entre guillemets. Si l'une ou l'autre était une chaîne multiligne, alors ils ne seraient pas en mesure de stocker des chaînes multilignes (ce qu'ils peuvent). On ne sait pas quel problème vous essayez de résoudre.
Kusalananda
2
Votre question revient à demander ce qu'est une @varvariable en Perl, en termes de stockage sous-jacent. Du point de vue d'un programme Perl ordinaire, cela n'a pas vraiment d'importance, sinon qu'il est accessible en tant que tableau / liste (et le fait qu'il existe des contextes dans lesquels une liste est attendue).
Kusalananda

Réponses:

16

Cela a commencé comme un hack dans le shell Bourne. Dans le shell Bourne, le fractionnement des mots IFS a été effectué (après la tokenisation) sur tous les mots dans le contexte de la liste (arguments de ligne de commande ou les mots sur lesquels les forboucles bouclent). Si tu avais:

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

Cette deuxième ligne serait en 3 mots en jetons, $varserait élargi, et divisé + glob serait fait sur les trois mots, de sorte que vous finiriez en cours d' exécution edavec t, f, le.txt, f, le2.txtcomme arguments.

Citer des parties de cela empêcherait le split + glob. Le shell Bourne se souvenait initialement des caractères cités en définissant le 8ème bit sur eux en interne (cela a changé plus tard lorsque Unix est devenu propre à 8 bits, mais le shell a toujours fait quelque chose de similaire pour se rappeler quel octet a été cité).

Les deux $*et $@étaient la concaténation des paramètres de position avec l'espace entre les deux. Mais il y avait un traitement spécial de l' $@intérieur des guillemets doubles. S'ils $1étaient contenus foo baret $2contenus baz, "$@"s'étendraient à:

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

(les ^s ci-dessus indiquant lesquels des caractères ont le 8ème bit défini). Où le premier espace a été cité (avait le 8ème bit défini) mais pas le second (celui ajouté entre les mots).

Et c'est le fractionnement IFS qui s'occupe de séparer les arguments (en supposant que le caractère espace est $IFScomme il est par défaut). C'est similaire à la façon dont a $*été développé dans son prédécesseur le shell Mashey (lui-même basé sur le shell Thomson, tandis que le shell Bourne a été écrit à partir de zéro).

Cela explique pourquoi dans le shell Bourne "$@"se développerait initialement dans la chaîne vide au lieu de rien du tout lorsque la liste des paramètres de position était vide (vous deviez le contourner ${1+"$@"}), pourquoi il n'a pas conservé les paramètres de position vides et pourquoi "$@"n'a pas $IFSne fonctionne pas quand ne contient pas le caractère espace.

L'intention était de pouvoir passer la liste des arguments textuellement à une autre commande, mais cela ne fonctionnait pas correctement pour la liste vide, pour les éléments vides ou quand $IFSne contenait pas d'espace (les deux premiers problèmes ont finalement été corrigés dans les versions ultérieures ).

Le shell Korn (sur lequel la spécification POSIX est basée) a modifié ce comportement de plusieurs manières:

  • Le fractionnement IFS ne se fait que sur le résultat d'expansions sans guillemets (pas sur des mots littéraux comme editou file.txtdans l'exemple ci-dessus)
  • $*et $@sont joints avec le premier caractère de $IFSou espace quand $IFSest vide sauf que pour un guillemet "$@", ce menuisier n'est pas entre guillemets comme dans le shell Bourne, et pour un guillemet "$*"quand IFSest vide, les paramètres de position sont ajoutés sans séparateur.
  • il a ajouté un support pour les tableaux, et avec des ${array[@]} ${array[*]}réminiscences de Bourne $*et $@mais commençant à l'indice 0 au lieu de 1, et clairsemé (plus comme des tableaux associatifs) ce qui signifie $@qu'il ne peut pas vraiment être traité comme un tableau ksh (comparer avec csh/ rc/ zsh/ fish/ yash$argv/ $*sont normaux tableaux).
  • Les éléments vides sont conservés.
  • "$@"quand $#est 0 se développe maintenant à rien au lieu de la chaîne vide, "$@"fonctionne quand $IFSne contient pas d'espaces sauf quand IFSest vide. Un $*caractère sans guillemets sans caractères génériques se développe en un seul argument (où les paramètres de position sont joints à l'espace) lorsqu'il $IFSest vide.

ksh93 a résolu les quelques problèmes restants ci-dessus. Dans ksh93, $*et se $@développe dans la liste des paramètres de position, séparés indépendamment de la valeur de $IFS, puis se divise davantage + globbed + accolade-développé dans les contextes de liste, $*joint avec le premier octet (pas de caractère) de $IFS, "$@"dans les contextes de liste se développe dans la liste des paramètres de position, quelle que soit la valeur de $IFS. Dans un contexte sans liste, comme dans var=$@, $@est joint à l'espace quelle que soit la valeur de $IFS.

bashLes tableaux de sont conçus après ceux de ksh. Les différences sont les suivantes:

  • aucune accolade-expansion lors d'une expansion non cotée
  • premier caractère de $IFSau lieu de pour octet
  • certaines différences de cas d'angle comme l'expansion de $*quand non-cité dans un contexte non-liste quand $IFSest vide.

Alors que la spécification POSIX était assez vague, elle spécifie maintenant plus ou moins le comportement de bash.

C'est différent des tableaux normaux dans kshou bashen ce que:

  • Les indices commencent à 1 au lieu de 0 (sauf dans "${@:0}"ce qui inclut $0(pas un paramètre positionnel, et dans les fonctions vous donne le nom de la fonction ou non selon le shell et comment la fonction a été définie)).
  • Vous ne pouvez pas attribuer des éléments individuellement
  • ce n'est pas rare, vous ne pouvez pas supprimer les éléments individuellement
  • shift peut être utilisé.

Dans zshou yashoù les tableaux sont des tableaux normaux (pas clairsemés, les indices commencent à un comme dans tous les autres shells mais ksh / bash), $*est traité comme un tableau normal. zsha $argvcomme alias (pour compatibilité avec csh). $*est identique à $argvor ${argv[*]}(arguments joints au premier caractère de $IFSmais toujours séparés dans les contextes de liste). "$@"comme "${argv[@]}"ou "${*[@]}"}subit le traitement spécial de style Korn.

Stéphane Chazelas
la source
8

Cependant, je ne sais pas ce qu'est la structure des données $@.

C'est un paramètre spécial qui se développe aux valeurs des paramètres de position ... Mais c'est une tergiversation sur la terminologie.

Nous pouvons voir les paramètres positionnels comme des parties de $@, donc il a un certain nombre d'éléments distincts ( $1, $2...), qui peuvent être accédés indépendamment et sont nommés par des nombres naturels consécutifs. Cela en fait quelque chose qui est généralement appelé un tableau.

La syntaxe est cependant un peu bizarre et même limitée. Il n'y a aucun moyen de modifier un seul élément du tableau individuellement. Au lieu de cela, le tout doit être réglé en même temps. (Vous pouvez utiliser set -- "$@" foopour ajouter une valeur, ou set -- "${@:1:2}" foo "${@:3}"pour ajouter une valeur au milieu. Mais dans les deux cas, vous devez écrire toute la liste résultante.)

Pourquoi cela se comporte-t-il différemment avec l' $*inclusion de guillemets doubles,

Parce qu'ils sont définis pour se comporter différemment.

Cependant, il peut également résonner entièrement avec simple echo $@, s'il s'agit d'un tableau, seul le premier élément sera affiché.

Si vous voulez dire le fait que la a=(foo bar asdf); echo $asortie sera juste foo, alors c'est principalement une bizarrerie de la syntaxe du shell, et le fait que les tableaux nommés de style ksh ont été créés plus tard que les paramètres positionnels et $@. Plain $aest le même que ${a[0]}s'il a la signification rétrocompatible d'une seule valeur scalaire, qu'il s'agisse d' aun tableau ou d'une simple variable scalaire.

Le @signe se référant à la liste entière a été réutilisé avec des tableaux nommés dans "${a[@]}"la manière d'obtenir la liste entière. Par rapport aux tableaux nommés, avec $@, les accolades et crochets inutiles et le nom sont simplement ignorés.

Ou en d'autres termes, je veux savoir comment les $@stocker dans la mémoire de l'ordinateur.

Cela dépend de l'implémentation, vous devrez regarder le code source de tout shell particulier qui vous intéresse.

Est-ce une chaîne, une chaîne multi-lignes ou un tableau?

Un tableau, surtout. Bien qu'ils soient différents des tableaux nommés de style ksh, car ils peuvent avoir des entiers non négatifs arbitraires comme index, pas seulement des entiers consécutifs comme avec $@. (Autrement dit, un tableau nommé peut être clairsemé et avoir, par exemple, les index 1, 3et 4, avec 0et 2manquant. Ce n'est pas possible avec les paramètres de position.)

Ce n'est pas une chaîne unique, car elle peut être étendue à des éléments distincts, et appeler les lignes d'éléments n'est pas non plus correct, car toute variable régulière ou l'un des paramètres de position (éléments de $@) peut également contenir des retours à la ligne.

S'il s'agit d'un type de données unique, est-il possible de définir une variable personnalisée comme instance de ce type?

Non. Mais les tableaux nommés sont probablement plus utiles de toute façon.

ilkkachu
la source
1
+1. TL: DR $@n'est pas une structure de données, c'est l'une des quelques fonctions / opérateurs pour étendre la structure de données des paramètres de position.
Peter Cordes