Test auf Array-Unterstützung durch Shell

11

Gibt es eine präzise Möglichkeit, die Array-Unterstützung durch die lokale Bourne-ähnliche Shell in der Befehlszeile zu testen?

Das ist immer möglich:

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

oder Testen für $SHELLund Shell-Version:

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

und dann die Manpage lesen, vorausgesetzt ich habe Zugriff darauf. (Selbst dort, /bin/bashwenn ich von schreibe , gehe ich davon aus, dass alle Bourne-ähnlichen Muscheln die lange Option zulassen --version, wenn dies zum Beispiel für ksh bricht .)

Ich suche nach einem einfachen Test, der unbeaufsichtigt sein und zu Beginn des Skripts oder sogar vor dem Aufruf in einen Verwendungsabschnitt aufgenommen werden kann.

Cbhihe
quelle
Ich nehme an, Sie möchten sich auf Bourne-ähnliche Muscheln beschränken?
Stéphane Chazelas
@ StéphaneChazelas: Ja, wenn Sie (nicht erschöpfend) die Kerngruppe meinen, bestehend aus: sh, csh, ksh, tcsh, bash, zsh und engen Freunden. Ich weiß nicht, wo sich Yash in dieser Konstellation positioniert.
Cbhihe
2
cshist keine Bourne Shell. tcshist auch keiner (es sind csheinige Fehler behoben)
Cas
1
Beachten Sie, dass dies $SHELLdie bevorzugte Shell des Benutzers ist, ebenso wie $EDITORsein bevorzugter Texteditor. Es hat wenig mit der aktuell laufenden Shell zu tun.
Stéphane Chazelas
1
evalDie Ausgabe von $SHELL --versionals Shell-Code zu verwenden, macht keinen Sinn.
Stéphane Chazelas

Antworten:

12

Angenommen , Sie zu Bourne-wie Muscheln einschränken wollen (viele andere Muscheln mögen csh, tcsh, rc, esoder fishSupport - Arrays aber ein Skript kompatibel zur gleichen Zeit zu Bourne artigen Schalen zu schreiben und das ist heikel und in der Regel sinnlos , da sie Dolmetscher sind für ganz anderen und Inkompatible Sprachen). Beachten Sie, dass zwischen den Implementierungen erhebliche Unterschiede bestehen.

Die Bourne-ähnlichen Shells, die Arrays unterstützen, sind:

  • ksh88(Dies ist das erste, das Arrays implementiert. Ksh88 ist immer noch kshauf den meisten herkömmlichen kommerziellen Unices zu finden, für die es auch die Basis ist. sh)

    • Arrays sind eindimensional
    • Arrays sind definiert als set -A array foo baroder set -A array -- "$var" ...wenn Sie nicht garantieren können, dass dies $varnicht mit einem -oder beginnt +.
    • Array-Indizes beginnen bei 0.
    • Einzelne Array-Elemente werden als zugewiesen a[1]=value.
    • Arrays sind spärlich. Das a[5]=foofunktioniert auch, wenn sie a[0,1,2,3,4]nicht eingestellt sind, und lässt sie nicht gesetzt.
    • ${a[5]}um auf das Element von Index 5 zuzugreifen (nicht unbedingt das 6. Element, wenn das Array dünn ist). Das 5kann jeder arithmetische Ausdruck sein.
    • Arraygröße und Index sind begrenzt (auf 4096).
    • ${#a[@]} ist die Anzahl der zugewiesenen Elemente im Array (nicht der größte zugewiesene Index).
    • Es gibt keine Möglichkeit, die Liste der zugewiesenen Indizes zu kennen (außer die 4096-Elemente einzeln mit zu testen [[ -n "${a[i]+set}" ]]).
    • $aist das gleiche wie ${a[0]}. Das heißt, Arrays erweitern irgendwie skalare Variablen, indem sie ihnen zusätzliche Werte geben.
  • pdkshund Derivate (das ist die Basis für die kshund manchmal shfür mehrere BSDs und war die einzige OpenSource-Implementierung von ksh, bevor die Quelle ksh93 freigegeben wurde):

    Meistens wie ksh88aber beachten Sie:

    • Einige alte Implementierungen wurden nicht unterstützt set -A array -- foo bar(die wurden --dort nicht benötigt).
    • ${#a[@]}ist eins plus der Index des größten zugewiesenen Index. ( a[1000]=1; echo "${#a[@]}"gibt 1001 aus, obwohl das Array nur ein Element enthält.
    • In neueren Versionen ist die Arraygröße nicht mehr begrenzt (außer durch die Größe von Ganzzahlen).
    • neuere Versionen von mkshhaben ein paar zusätzlichen Betreiber von inspiriert bash, ksh93oder zshwie Zuweisungen a la a=(x y), a+=(z), ${!a[@]}die Liste des zugewiesenen Indizes zu erhalten.
  • zsh. zshArrays sind im Allgemeinen besser gestaltet und nutzen das Beste aus kshund cshArrays. Sie sind ähnlich, kshaber mit signifikanten Unterschieden:

    • Indizes beginnen bei 1, nicht bei 0 (außer bei der kshEmulation), was mit dem Bourne-Array (den Positionsparametern $ @, das zshauch als $ argv-Array verfügbar gemacht wird) und den cshArrays übereinstimmt .
    • Sie sind ein separater Typ von normalen / skalaren Variablen. Operatoren gelten für sie anders und wie Sie es allgemein erwarten würden. $aist nicht dasselbe wie, ${a[0]}sondern erweitert sich auf die nicht leeren Elemente des Arrays ( "${a[@]}"für alle Elemente wie in ksh).
    • Sie sind normale Arrays, keine spärlichen Arrays. a[5]=1funktioniert, weist aber allen Elementen von 1 bis 4 die leere Zeichenfolge zu, wenn sie nicht zugewiesen wurden. Also ${#a[@]}(genau wie ${#a}in ksh die Größe des Elements des Index 0) ist die Anzahl der Elemente im Array und der größte zugewiesene Index.
    • assoziative Arrays werden unterstützt.
    • Eine große Anzahl von Operatoren für die Arbeit mit Arrays wird unterstützt, zu groß, um sie hier aufzulisten.
    • Arrays definiert als a=(x y). set -A a x yfunktioniert auch, wird aber set -A a -- x ynur in der ksh-Emulation unterstützt (dies --wird in der zsh-Emulation nicht benötigt).
  • ksh93. (Hier werden die neuesten Versionen beschrieben). ksh93Das seit langem als experimentell angesehene Experiment findet sich nun in immer mehr Systemen, nachdem es als FOSS veröffentlicht wurde. Zum Beispiel ist es das /bin/sh(wo es die Bourne-Shell ersetzt hat /usr/xpg4/bin/sh, auf der die POSIX-Shell noch basiert ksh88) und kshvon Solaris 11. Seine Arrays erweitern und verbessern ksh88.

    • a=(x y)kann verwendet werden, um ein Array zu definieren, aber da a=(...)es auch zum Definieren von zusammengesetzten Variablen ( a=(foo=bar bar=baz)) verwendet wird, a=()ist es mehrdeutig und deklariert eine zusammengesetzte Variable, kein Array.
    • Arrays sind mehrdimensional ( a=((0 1) (0 2))) und Array-Elemente können auch zusammengesetzte Variablen ( a=((a b) (c=d d=f)); echo "${a[1].c}") sein.
    • Eine a=([2]=foo [5]=bar)Syntax kann verwendet werden, um dünn besetzte Arrays gleichzeitig zu definieren.
    • Größenbeschränkungen aufgehoben.
    • Nicht im Umfang zsh, aber eine große Anzahl von Operatoren wird auch unterstützt, um Arrays zu manipulieren.
    • "${!a[@]}" um die Liste der Array-Indizes abzurufen.
    • assoziative Arrays werden auch als separater Typ unterstützt.
  • bash. bashist die Hülle des GNU-Projekts. Es wird wie shin neueren Versionen von OS / X und einigen GNU / Linux-Distributionen verwendet. bashArrays emulieren meistens ksh88solche mit einigen Merkmalen von ksh93und zsh.

    • a=(x y)unterstützt. set -A a x y nicht unterstützt. a=()Erstellt ein leeres Array (keine zusammengesetzten Variablen in bash).
    • "${!a[@]}" für die Liste der Indizes.
    • a=([foo]=bar)Syntax unterstützt sowie einige andere von ksh93und zsh.
    • Neuere bashVersionen unterstützen auch assoziative Arrays als separaten Typ.
  • yash. Es ist eine relativ neue, saubere, Multi-Byte-fähige POSIX sh-Implementierung. Nicht weit verbreitet. Seine Arrays sind eine weitere saubere API ähnlichzsh

    • Arrays sind nicht spärlich
    • Array-Indizes beginnen bei 1
    • definiert (und deklariert) mit a=(var value)
    • Elemente eingefügt, gelöscht oder geändert mit dem arrayeingebauten
    • array -s a 5 valuedie 5 zu ändern ten Element würde fehlschlagen , wenn das Element nicht vorher zugewiesen wurde.
    • die Anzahl der Elemente in dem Array ist ${a[#]}, ${#a[@]}als eine Liste der Größe der Elemente zu sein.
    • Arrays sind ein separater Typ. Sie müssen a=("$a")eine skalare Variable als Array neu definieren, bevor Sie Elemente hinzufügen oder ändern können.
    • Arrays werden beim Aufrufen als nicht unterstützt sh.

Daraus können Sie die Erkennung der Array-Unterstützung ersehen, mit der Sie Folgendes tun können:

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

reicht nicht aus, um diese Arrays verwenden zu können. Sie müssen Wrapper-Befehle definieren, um Arrays als Ganzes und einzelne Elemente zuzuweisen, und sicherstellen, dass Sie nicht versuchen, spärliche Arrays zu erstellen.

Mögen

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

Und dann greifen Sie auf Array - Elemente mit "${a[$first_indice+n]}", die ganze Liste mit "${a[@]}"und verwenden Sie die Wrapper - Funktionen ( array_elements, set_array, set_array_element) die Anzahl der Elemente eines Arrays zu erhalten (in $REPLY), setzen Sie das Array als Ganzes oder assign einzelnen Elemente.

Wahrscheinlich nicht die Mühe wert. Ich würde perldas Bourne / POSIX-Shell-Array verwenden oder darauf beschränken : "$@".

Wenn die Absicht besteht, dass eine Datei von der interaktiven Shell eines Benutzers bezogen wird, um Funktionen zu definieren, die intern Arrays verwenden, finden Sie hier einige weitere nützliche Hinweise.

Sie können zshArrays so konfigurieren , dass sie kshArrays in lokalen Bereichen (in Funktionen oder anonymen Funktionen) ähneln.

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

Sie können auch emulieren ksh(die Kompatibilität mit kshArrays und mehreren anderen Bereichen verbessern ) mit:

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

In diesem Sinne und Sie sind bereit, die Unterstützung für yashund ksh88ältere Versionen von pdkshDerivaten einzustellen. Solange Sie nicht versuchen, spärliche Arrays zu erstellen, sollten Sie in der Lage sein, konsequent Folgendes zu verwenden:

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

in jenen Funktionen, die das haben emulate -L ksh, während der zshBenutzer seine Arrays normalerweise noch auf zsh-Weise benutzt.

Stéphane Chazelas
quelle
7

Sie können evaldie Array-Syntax ausprobieren:

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

if is_array_support; then
  echo support
else
  echo not
fi
cuonglm
quelle
2
ksh88unterstützt Arrays aber nicht a=(). In ksh93, a=()deklariert eine Verbindung variabel, kein Array es sei denn , die Variable vorher als Array erklärt wurde.
Stéphane Chazelas
2
Beachten Sie auch, dass es signifikante Unterschiede zwischen Array-Implementierungen gibt. Zum Beispiel haben einige Array-Indizes, die bei 0 beginnen (bash, ksh, zsh in der ksh-Emulation), andere beginnen bei eins (zsh, yash). Einige sind normale Arrays / Listen, andere sind spärliche Arrays (assoziative Arrays mit Schlüsseln, die auf positive ganze Zahlen wie in ksh oder bash beschränkt sind).
Stéphane Chazelas
In yash, Sie tun nicht, a[5]=1aberarray -s a 5 1
Stéphane Chazelas
@ StéphaneChazelas: Danke für die Präzision. In meinem Fall läuft alles darauf hinaus, ob Arrays (assoziativ oder nicht) überhaupt unterstützt werden. Details zur Indexbasis können auch in einem Skript, das unbeaufsichtigt ausgeführt werden soll, problemlos ausgearbeitet werden.
Cbhihe
@ StéphaneChazelas: Die zusammengesetzte Variable in ksh93hat mich überrascht. Würde es Ihnen etwas ausmachen, mir einen Teil der Dokumentation darüber zu geben? Ich füge 1dem Array hinzu, damit es funktioniert.
Cuonglm