Test per il supporto dell'array tramite shell


12

Esiste un modo conciso di test per il supporto dell'array da parte della shell locale simile a Bourne alla riga di comando?

Questo è sempre possibile:

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

o test per $SHELLe versione shell:

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

e poi leggendo la pagina man, supponendo che io abbia accesso ad essa. (Anche lì, scrivendo da /bin/bash, presumo che tutte le shell tipo Bourne ammettano la lunga opzione --version, quando ciò si interrompe per ksh per esempio .)

Sto cercando un semplice test che potrebbe essere incustodito e incorporato in una sezione di utilizzo all'inizio dello script o anche prima di chiamarlo.


Presumo che tu voglia limitare a gusci tipo Bourne?
Stéphane Chazelas,

@ StéphaneChazelas: Sì, se intendi (non esaustivamente) il gruppo principale composto da: sh, csh, ksh, tcsh, bash, zsh e amici intimi. Non so dove si posiziona lo yash in questa costellazione.
Cbhihe,

2
cshnon è un guscio bourne. tcshneanche uno (è cshcon alcuni bug corretti)
cas

1
Nota che $SHELLè la shell preferita dell'utente, come $EDITORè il suo editor di testo preferito. Ha poco a che fare con la shell attualmente in esecuzione.
Stéphane Chazelas,

1
evaluu l'output di $SHELL --versioncome codice shell non ha senso.
Stéphane Chazelas,

Risposte:


12

Dando per scontato che desidera limitare a Bourne-come conchiglie (molti altri shells Ti piace csh, tcsh, rc, eso fishmatrici di supporto, ma scrivendo uno script compatibili allo stesso tempo di shell Bourne-like e chi è ingannevole e, in generale inutile in quanto sono interpreti per completamente diverso e linguaggi incompatibili), si noti che esistono differenze significative tra le implementazioni.

Le shell tipo Bourne che supportano gli array sono:

  • ksh88(Questo è il primo ad implementare array, ksh88 si trova ancora come kshnella maggior parte degli Unici commerciali tradizionali dove è anche la base per sh)

    • le matrici sono monodimensionali
    • Le matrici sono definite come set -A array foo baro set -A array -- "$var" ...se non è possibile garantire che $varnon inizieranno con un -o +.
    • Gli indici di array iniziano alle 0.
    • I singoli elementi dell'array sono assegnati come a[1]=value.
    • le matrici sono sparse. Funzionerà a[5]=fooanche se a[0,1,2,3,4]non sono impostati e li lascerà non impostati.
    • ${a[5]}per accedere all'elemento indice 5 (non necessariamente al sesto elemento se l'array è scarso). La 5non ci può essere qualsiasi espressione aritmetica.
    • la dimensione e la sottoscrizione dell'array sono limitate (a 4096).
    • ${#a[@]} è il numero di elementi assegnati nell'array (non il massimo indice assegnato).
    • non è possibile conoscere l'elenco degli indici assegnati (se non quello di testare individualmente i 4096 elementi [[ -n "${a[i]+set}" ]]).
    • $aè lo stesso di ${a[0]}. Cioè gli array in qualche modo estendono le variabili scalari dando loro valori extra.
  • pdkshe derivati ​​(questa è la base per kshe talvolta shdi diversi BSD ed era l'unica implementazione open source di ksh prima che la fonte ksh93 fosse liberata):

    Principalmente come ksh88ma nota:

    • Alcune vecchie implementazioni non supportavano set -A array -- foo bar( --non era necessario lì).
    • ${#a[@]}è uno più l'indice del massimo indice assegnato. ( a[1000]=1; echo "${#a[@]}"genera 1001 anche se l'array ha solo un elemento.
    • nelle versioni più recenti, la dimensione dell'array non è più limitata (tranne che dalla dimensione degli interi).
    • le versioni recenti di mkshavere pochi operatori in più ispirate da bash, ksh93o zshcome le assegnazioni a la a=(x y), a+=(z), ${!a[@]}per ottenere l'elenco degli indici assegnati.
  • zsh. zshle matrici sono generalmente progettate meglio e prendono il meglio kshe le cshmatrici. Sono simili kshma con differenze significative:

    • gli indici iniziano da 1, non da 0 (tranne che kshnell'emulazione), in linea con l'array Bourne (i parametri di posizione $ @, che zshespone anche come array $ argv) e gli csharray.
    • sono un tipo separato dalle variabili normali / scalari. Gli operatori si applicano in modo diverso a loro e come ti aspetteresti generalmente. $anon è lo stesso di ${a[0]}ma si espande negli elementi non vuoti dell'array ( "${a[@]}"per tutti gli elementi come in ksh).
    • sono matrici normali, non matrici sparse. a[5]=1funziona ma assegna a tutti gli elementi da 1 a 4 la stringa vuota se non sono stati assegnati. Quindi ${#a[@]}(lo stesso del ${#a}quale in ksh è la dimensione dell'elemento di indice 0) è il numero di elementi nell'array e il massimo indice assegnato.
    • matrici associative sono supportate.
    • è supportato un gran numero di operatori per lavorare con array, troppo grandi per essere elencati qui.
    • matrici definite come a=(x y). set -A a x yfunziona anche, ma set -A a -- x ynon è supportato se non nell'emulazione ksh ( --non è necessaria nell'emulazione zsh).
  • ksh93. (qui che descrivono le ultime versioni). ksh93, considerato a lungo sperimentale ora può essere trovato in sempre più sistemi ora che è stato rilasciato come FOSS. Ad esempio, è il /bin/sh(dove ha sostituito la shell Bourne /usr/xpg4/bin/sh, la shell POSIX è ancora basata su ksh88) e kshdi Solaris 11. I suoi array si estendono e migliorano quelli di ksh88.

    • a=(x y)può essere usato per definire una matrice, ma poiché a=(...)viene anche usato per definire variabili composte ( a=(foo=bar bar=baz)), a=()è ambiguo e dichiara una variabile composta, non una matrice.
    • le matrici sono multidimensionali ( a=((0 1) (0 2))) e gli elementi di matrice possono anche essere variabili composte ( a=((a b) (c=d d=f)); echo "${a[1].c}").
    • Una a=([2]=foo [5]=bar)sintassi può essere utilizzata per definire array sparsi contemporaneamente.
    • Limiti di dimensione sollevati.
    • Non nella misura in cui zsh, ma un gran numero di operatori ha supportato anche per manipolare array.
    • "${!a[@]}" per recuperare l'elenco di indici di array.
    • matrici associative supportate anche come tipo separato.
  • bash. bashè la shell del progetto GNU. È usato come shnelle recenti versioni di OS / X e alcune distribuzioni GNU / Linux. bashle matrici emulano principalmente ksh88quelle con alcune caratteristiche di ksh93e zsh.

    • a=(x y)supportato. set -A a x y non supportato. a=()crea un array vuoto (nessuna variabile composta in bash).
    • "${!a[@]}" per l'elenco degli indici.
    • a=([foo]=bar)sintassi supportata e alcune altre da ksh93e zsh.
    • le bashversioni recenti supportano anche array associativi come tipo separato.
  • yash. Si tratta di un'implementazione sh POSIX relativamente recente, pulita e compatibile con più byte. Non ampiamente utilizzato. I suoi array sono un'altra API pulita simile azsh

    • le matrici non sono sparse
    • Gli indici di array iniziano da 1
    • definito (e dichiarato) con a=(var value)
    • elementi inseriti, cancellati o modificati con l' arrayintegrato
    • array -s a 5 valuela modifica del 5 ° elemento fallirebbe se quell'elemento non fosse stato assegnato in anticipo.
    • il numero di elementi nell'array è ${a[#]}, ${#a[@]}essendo la dimensione degli elementi come un elenco.
    • le matrici sono di tipo separato. È necessario a=("$a")ridefinire una variabile scalare come matrice prima di poter aggiungere o modificare elementi.
    • le matrici non sono supportate quando invocate come sh.

Quindi, da questo puoi vedere quel rilevamento per il supporto dell'array, che potresti fare con:

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

non è sufficiente per poter usare quelle matrici. Dovresti definire i comandi wrapper per assegnare matrici come elementi interi e singoli e assicurarti di non tentare di creare matrici sparse.

Piace

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

E poi accedere con elementi dell'array "${a[$first_indice+n]}", l'intero elenco con "${a[@]}"e utilizzare le funzioni wrapper ( array_elements, set_array, set_array_element) per ottenere il numero di elementi di una matrice (a $REPLY), impostare la matrice come un intero o assegnare singoli elementi.

Probabilmente non vale la pena. Userei perlo limite alla matrice shell Bourne / POSIX: "$@".

Se l'intento è quello di avere alcuni file che devono essere forniti dalla shell interattiva di un utente per definire le funzioni che utilizzano internamente le matrici, ecco alcune altre note che potrebbero essere utili.

È possibile configurare gli zsharray in modo che siano più simili agli ksharray negli ambiti locali (in funzioni o funzioni anonime).

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

È inoltre possibile emulare ksh(migliorare la compatibilità con ksharray e diverse altre aree) con:

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

Con questo in mente e si è disposti a rilasciare il supporto per yashe ksh88e le versioni precedenti di pdkshderivati, e finché non si tenta di creare array sparsi, si dovrebbe essere in grado di utilizzare in modo coerente:

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

in quelle funzioni che hanno emulate -L ksh, mentre l' zshutente usa ancora le sue matrici normalmente nel modo zsh.


7

È possibile utilizzare evalper provare la sintassi dell'array:

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

if is_array_support; then
  echo support
else
  echo not
fi

2
ksh88supporta array ma non a=(). In ksh93, a=()dichiara una variabile composta, non una matrice a meno che la variabile non sia stata precedentemente dichiarata come matrice.
Stéphane Chazelas,

2
Si noti inoltre che esistono differenze significative tra le implementazioni di array. Ad esempio, alcuni hanno indici di array che iniziano da 0 (bash, ksh, zsh nell'emulazione ksh), altri che iniziano da uno (zsh, yash). Alcuni sono array / liste normali, altri sono array sparsi (array associativi con chiavi limitate a numeri interi positivi come in ksh o bash).
Stéphane Chazelas,

In yash, non lo fai a[5]=1maarray -s a 5 1
Stéphane Chazelas,

@ StéphaneChazelas: grazie per le precisazioni. Nel mio caso tutto si riduce al fatto che gli array (associativi o meno) siano supportati affatto. I dettagli sulla base dell'indice possono essere facilmente elaborati anche in uno script destinato a essere eseguito incustodito.
Cbhihe,

@ StéphaneChazelas: La variabile ksh93composta in mi ha fatto sorprendere, ti dispiacerebbe darmi parte della documentazione a riguardo. Aggiungo 1l'array per farlo funzionare.
cuonglm,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.