Test de prise en charge des baies par shell

11

Existe-t-il un moyen concis de tester la prise en charge des tableaux par le shell local de type Bourne sur la ligne de commande?

C'est toujours possible:

$ arr=(0 1 2 3);if [ "${arr[2]}" != 2 ];then echo "No array support";fi

ou tester $SHELLet version shell:

$ eval $(echo "$SHELL --version") | grep version

puis en lisant la page de manuel, en supposant que j'y ai accès. (Même là, en écrivant de /bin/bash, je suppose que tous les shells de type Bourne admettent l'option longue --version, quand cela casse pour ksh par exemple .)

Je recherche un test simple qui pourrait être sans surveillance et incorporé dans une section Utilisation au début du script ou même avant de l'appeler.

Cbhihe
la source
Je suppose que vous voulez vous limiter aux obus de type Bourne?
Stéphane Chazelas
@ StéphaneChazelas: Oui, si vous voulez dire (pas de manière exhaustive) le groupe central composé de: sh, csh, ksh, tcsh, bash, zsh et des amis proches. Je ne sais pas où yash se positionne dans cette constellation.
Cbhihe
2
cshn'est pas une coquille de bourne. tcshn'en est pas un non plus (c'est cshavec quelques bugs corrigés)
cas
1
Notez que $SHELLc'est le shell préféré de l'utilisateur, tout comme $EDITORson éditeur de texte préféré. Cela n'a pas grand-chose à voir avec le shell en cours d'exécution.
Stéphane Chazelas
1
evalL'utilisation de la sortie du $SHELL --versioncode shell n'a pas de sens.
Stéphane Chazelas

Réponses:

12

En supposant que vous voulez limiter à Bourne , comme des coquillages (beaucoup d' autres interpréteurs tels que csh, tcsh, rc, esou des fishréseaux de soutien , mais l' écriture d' un script compatible en même temps de Bourne comme des coquilles et celles -ci est difficile et généralement inutile car ils sont les interprètes pour tout autre et incompatibles), notez qu'il existe des différences importantes entre les implémentations.

Les shells Bourne qui supportent les tableaux sont:

  • ksh88(c'est le premier à implémenter des tableaux, ksh88 se trouve toujours comme kshsur la plupart des Unités Commerciales traditionnelles où il sert également de base sh)

    • les tableaux sont unidimensionnels
    • Les tableaux sont définis comme set -A array foo barou set -A array -- "$var" ...si vous ne pouvez pas garantir que $varcela ne commencera pas par un -ou +.
    • Les indices de tableau commencent à 0.
    • Les éléments de tableau individuels sont affectés en tant que a[1]=value.
    • les tableaux sont clairsemés. Cela a[5]=foofonctionnera même si a[0,1,2,3,4]elles ne sont pas définies et les laissera non définies.
    • ${a[5]}pour accéder à l'élément d'indice 5 (pas nécessairement le 6ème élément si le tableau est clairsemé). L' 5il peut y avoir une expression arithmétique.
    • la taille du tableau et l'indice sont limités (à 4096).
    • ${#a[@]} est le nombre d'éléments attribués dans le tableau (et non le plus grand indice attribué).
    • il n'y a aucun moyen de connaître la liste des indices attribués (à part tester les 4096 éléments individuellement avec [[ -n "${a[i]+set}" ]]).
    • $aest le même que ${a[0]}. C'est-à-dire que les tableaux étendent en quelque sorte les variables scalaires en leur donnant des valeurs supplémentaires.
  • pdkshet dérivés (c'est la base de kshet parfois shde plusieurs BSD et était la seule implémentation ksh open source avant que la source ksh93 ne soit libérée):

    Surtout comme ksh88mais notez:

    • Certaines anciennes implémentations ne prenaient pas en charge set -A array -- foo bar(ce --n'était pas nécessaire là-bas).
    • ${#a[@]}est un plus l'indice du plus grand indice attribué. ( a[1000]=1; echo "${#a[@]}"génère 1001 même si le tableau n'a qu'un seul élément.
    • dans les versions plus récentes, la taille du tableau n'est plus limitée (autrement que par la taille des entiers).
    • les versions récentes de mkshont quelques opérateurs supplémentaires inspirés de bash, ksh93ou zshcomme les affectations à la a=(x y), a+=(z), ${!a[@]}pour obtenir la liste des indices attribués.
  • zsh. zshles tableaux sont généralement mieux conçus et prennent le meilleur de kshet les cshtableaux. Ils sont similaires kshmais avec des différences importantes:

    • les indices commencent à 1, pas à 0 (sauf en kshémulation), ce qui est cohérent avec le tableau Bourne (les paramètres de position $ @, qui zshexpose également comme son tableau $ argv) et les cshtableaux.
    • ils sont d'un type distinct des variables normales / scalaires. Les opérateurs s'appliquent différemment à eux et comme vous vous y attendez généralement. $an'est pas identique à ${a[0]}mais s'étend aux éléments non vides du tableau ( "${a[@]}"pour tous les éléments comme dans ksh).
    • ce sont des tableaux normaux, pas des tableaux clairsemés. a[5]=1fonctionne mais affecte tous les éléments de 1 à 4 à la chaîne vide s'ils n'ont pas été affectés. Donc ${#a[@]}(identique à ${#a}celui dans ksh la taille de l'élément de l'indice 0) est le nombre d'éléments dans le tableau et le plus grand indice attribué.
    • les tableaux associatifs sont pris en charge.
    • un grand nombre d'opérateurs pour travailler avec des tableaux est pris en charge, trop gros pour être répertorié ici.
    • tableaux définis comme a=(x y). set -A a x yfonctionne également, mais set -A a -- x yn'est pas pris en charge sauf dans l'émulation ksh (ce --n'est pas nécessaire dans l'émulation zsh).
  • ksh93. (décrivant ici les dernières versions). ksh93, longtemps considéré comme expérimental, peut maintenant être trouvé dans de plus en plus de systèmes maintenant qu'il a été publié sous le nom de FOSS. Par exemple, c'est le /bin/sh(où il a remplacé le shell Bourne /usr/xpg4/bin/sh, le shell POSIX est toujours basé sur ksh88) et kshde Solaris 11. Ses tableaux étendent et améliorent les ksh88.

    • a=(x y)peut être utilisé pour définir un tableau, mais puisqu'il a=(...)est également utilisé pour définir des variables composées ( a=(foo=bar bar=baz)), a=()est ambigu et déclare une variable composée, pas un tableau.
    • les tableaux sont multidimensionnels ( a=((0 1) (0 2))) et les éléments du tableau peuvent également être des variables composées ( a=((a b) (c=d d=f)); echo "${a[1].c}").
    • Une a=([2]=foo [5]=bar)syntaxe peut être utilisée pour définir simultanément des tableaux clairsemés.
    • Levée des limitations de taille.
    • Pas dans la mesure de zsh, mais un grand nombre d'opérateurs pris en charge également pour manipuler les tableaux.
    • "${!a[@]}" pour récupérer la liste des indices de tableau.
    • les tableaux associatifs sont également pris en charge comme un type distinct.
  • bash. bashest l'enveloppe du projet GNU. Il est utilisé comme shsur les versions récentes d'OS / X et certaines distributions GNU / Linux. bashles tableaux imitent principalement ksh88ceux avec certaines fonctionnalités de ksh93et zsh.

    • a=(x y)prise en charge. set -A a x y non pris en charge. a=()crée un tableau vide (aucune variable composée dans bash).
    • "${!a[@]}" pour la liste des indices.
    • a=([foo]=bar)syntaxe prise en charge ainsi que quelques autres de ksh93et zsh.
    • les bashversions récentes prennent également en charge les tableaux associatifs en tant que type distinct.
  • yash. Il s'agit d'une implémentation POSIX sh relativement récente, propre et multi-octets. Pas largement utilisé. Ses tableaux sont une autre API propre similaire àzsh

    • les tableaux ne sont pas rares
    • Les indices de tableau commencent à 1
    • défini (et déclaré) avec a=(var value)
    • éléments insérés, supprimés ou modifiés avec la fonction arrayintégrée
    • array -s a 5 valuemodifier le 5 e élément échouerait si cet élément n'était pas assigné au préalable.
    • le nombre d'éléments dans le tableau est ${a[#]}, ${#a[@]}étant la taille des éléments sous forme de liste.
    • les tableaux sont un type distinct. Vous devez a=("$a")redéfinir une variable scalaire en tant que tableau avant de pouvoir ajouter ou modifier des éléments.
    • les tableaux ne sont pas pris en charge lorsqu'ils sont appelés en tant que sh.

Donc, à partir de cela, vous pouvez voir que la détection de la prise en charge des baies, que vous pouvez faire avec:

if (unset a; set -A a a; eval "a=(a b)"; eval '[ -n "${a[1]}" ]'
   ) > /dev/null 2>&1
then
  array_supported=true
else
  array_supported=false
fi

n'est pas suffisant pour pouvoir utiliser ces tableaux. Vous devez définir des commandes d'encapsuleur pour affecter des tableaux dans leur ensemble et des éléments individuels, et assurez-vous de ne pas tenter de créer des tableaux clairsemés.

Comme

unset a
array_elements() { eval "REPLY=\"\${#$1[@]}\""; }
if (set -A a -- a) 2> /dev/null; then
  set -A a -- a b
  case ${a[0]}${a[1]} in
    --) set_array() { eval "shift; set -A $1"' "$@"'; }
        set_array_element() { eval "$1[1+(\$2)]=\$3"; }
        first_indice=0;;
     a) set_array() { eval "shift; set -A $1"' -- "$@"'; }
        set_array_element() { eval "$1[1+(\$2)]=\$3"; }
        first_indice=1;;
   --a) set_array() { eval "shift; set -A $1"' "$@"'; }
        set_array_element() { eval "$1[\$2]=\$3"; }
        first_indice=0;;
    ab) set_array() { eval "shift; set -A $1"' -- "$@"'; }
        set_array_element() { eval "$1[\$2]=\$3"; }
        first_indice=0;;
  esac
elif (eval 'a[5]=x') 2> /dev/null; then
  set_array() { eval "shift; $1=("'"$@")'; }
  set_array_element() { eval "$1[\$2]=\$3"; }
  first_indice=0
elif (eval 'a=(x) && array -s a 1 y && [ "${a[1]}" = y ]') 2> /dev/null; then
  set_array() { eval "shift; $1=("'"$@")'; }
  set_array_element() {
    eval "
      $1=(\${$1+\"\${$1[@]}"'"})
      while [ "$(($2))" -ge  "${'"$1"'[#]}" ]; do
        array -i "$1" "$2" ""
      done'
    array -s -- "$1" "$((1+$2))" "$3"
   }
  array_elements() { eval "REPLY=\${$1[#]}"; }
  first_indice=1
else
  echo >&2 "Array not supported"
fi

Et puis vous accédez à des éléments de tableau avec "${a[$first_indice+n]}", toute la liste avec "${a[@]}"et d' utiliser les fonctions d'emballage ( array_elements, set_array, set_array_element) pour obtenir le nombre d'éléments d'un tableau (en $REPLY), définissez le tableau dans son ensemble ou attribuer des éléments individuels.

Ne vaut probablement pas la peine. J'utilise perlou limiter au tableau Bourne shell / POSIX: "$@".

Si l'intention est de faire en sorte que certains fichiers soient fournis par le shell interactif d'un utilisateur pour définir des fonctions qui utilisent des tableaux en interne, voici quelques notes supplémentaires qui peuvent être utiles.

Vous pouvez configurer les zshtableaux pour qu'ils ressemblent davantage à des kshtableaux dans des étendues locales (dans des fonctions ou des fonctions anonymes).

myfunction() {
  [ -z "$ZSH_VERSION" ] || setopt localoption ksharrays
  # use arrays of indice 0 in this function
}

Vous pouvez également émuler ksh(améliorer la compatibilité avec les kshtableaux et plusieurs autres domaines) avec:

myfunction() {
  [ -z "$ZSH_VERSION" ] || emulate -L ksh
  # ksh code more likely to work here
}

Avec cela à l' esprit et vous êtes prêt à déposer soutien yashet ksh88et les anciennes versions de pdkshproduits dérivés, et aussi longtemps que vous ne pas essayer de créer des tableaux rares, vous devriez être en mesure d'utiliser de manière cohérente:

  • a[0]=foo
  • a=(foo bar)(mais pas a=())
  • "${a[#]}", "${a[@]}","${a[0]}"

dans ces fonctions qui ont le emulate -L ksh, tandis que l' zshutilisateur utilise toujours ses tableaux normalement la manière zsh.

Stéphane Chazelas
la source
7

Vous pouvez utiliser evalpour essayer la syntaxe du tableau:

is_array_support() (
  eval 'a=(1)'
) >/dev/null 2>&1

if is_array_support; then
  echo support
else
  echo not
fi
cuonglm
la source
2
ksh88prend en charge les tableaux mais pas a=(). Dans ksh93, a=()déclare une variable composée, pas un tableau, sauf si la variable a été déclarée auparavant comme un tableau.
Stéphane Chazelas
2
Notez également qu'il existe des différences importantes entre les implémentations de tableaux. Par exemple, certains ont des indices de tableau commençant à 0 (bash, ksh, zsh dans l'émulation ksh), certains commençant à un (zsh, yash). Certains sont des tableaux / listes normaux, certains sont des tableaux clairsemés (tableaux associatifs avec des clés limitées à des entiers positifs comme dans ksh ou bash).
Stéphane Chazelas
Dans yash, tu ne fais pas a[5]=1maisarray -s a 5 1
Stéphane Chazelas
@ StéphaneChazelas: merci pour les précisions. Dans mon cas, tout se résume à la prise en charge des tableaux (associatifs ou non). Les détails sur l'index-base peuvent être facilement élaborés même dans un script destiné à fonctionner sans surveillance.
Cbhihe
@ StéphaneChazelas: La variable ksh93composée m'a fait une surprise, cela vous dérangerait de me donner une partie de la documentation à ce sujet. J'ajoute 1au tableau pour le faire fonctionner.
cuonglm