# \[Bash\]\[Awk\] variable dans array

**URL:** <https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927>\
**Category:** Programmation\
**Created:** [Mars 30, 2016, 1:32 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927 "2016-03-30T13:32:52Z")\
**Posts on this page:** 19\
**Page:** 1

<div class="post-metadata">

**Author:** ![PengouinPdt](https://www.debian-fr.org/user_avatar/www.debian-fr.org/pengouinpdt/32/3978_2.png) [@PengouinPdt](https://www.debian-fr.org/u/PengouinPdt)\
**Post date:** [Mars 30, 2016, 1:32 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/1 "2016-03-30T13:32:52Z")

</div>

Mon problème est le suivant :  
Je lis le contenu d’un fichier, alias /etc/default/sslh, pour récupérer la ligne concernant les options du service, soit celle-ci :

```auto
DAEMON_OPTS="--user sslh --listen 192.168.xyz.abc:443 --ssh 127.0.0.1:22 --ssl 127.0.0.1:443 --pidfile /var/run/sslh/sslh.pid"
```

Est-il possible avec awk d’obtenir son contenu … oui.

```auto
awk -F '=' '/DAEMON_OPTS/ { print $2}' /etc/default/sslh | awk -F '"' '{ print $2 }'
```

me restitue bien :

```auto
 --user sslh --listen 192.168.xyz.abc:443 --ssh 127.0.0.1:22 --ssl 127.0.0.1:443 --pidfile /var/run/sslh/sslh.pid 
```

On doit même pouvoir concaténer les deux … non ?!

Ce que j’aimerais arrivé à faire est que, dès qu’il détecte un espace, cela remplisse une nouvelle variable.

Soit un tableau associatif, nommé SSLH\_INFOS, par exemple …  
Quand le script lit que l’écriture commence par ‹&nbsp;–&nbsp;›, il créé la variable, tel que SSLH\_INFOS[«&nbsp;user&nbsp;»], par exemple, et que l’écriture suivante devienne sa valeur !

Oui, je sais, c’est un peu complexe … mais je serais étonné que cela ne soit pas faisable.  
Quoiqu’il en soit, je tiens à ce que ce soit du Bash, et pas un autre langage de script 😉

* * *

> [@PengouinPdt](#):
>
> awk -F ‹&nbsp;=&nbsp;› ‹&nbsp;/DAEMON\_OPTS/ { print $2}&nbsp;› /etc/default/sslh | awk -F ‹&nbsp;"&nbsp;› ‹&nbsp;{ print $2 }&nbsp;›

Devient :  
` awk -F '"' '/DAEMON_OPTS/ { print $2 }' /etc/default/sslh`

* * *

Je n’y crois pas, j’y suis arrivé !!!

```auto
#!/bin/bash
#set -x
declare -A SSLH_INFOS
cmd="$(awk -F '"' '/DAEMON_OPTS/ { print $2 }' /etc/default/sslh)"
for element in ${cmd// / }; do
    if grep -q '^--' <<< "${element}"; then
    var="${element:2}"
    SSLH_INFOS+=( [${var}]="" )
fi

if grep -q -v '^--' <<< "${element}"; then
    SSLH_INFOS+=( [${var}]="${element}" )
fi
done
echo "${SSLH_INFOS["user"]}"
```

Champagne ! 👅

---

<div class="post-metadata">

**Author:** ![Watael](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/w/a4c791/32.png) [@Watael](https://www.debian-fr.org/u/Watael)\
**Post date:** [Mars 30, 2016, 9:36 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/4 "2016-03-30T21:36:43Z")

</div>

salut,

[code]$ awk\_output=‹&nbsp;–user sslh --listen 192.168.147.3:443 --ssh 127.0.0.1:247 --ssl 127.0.0.1:443 --pidfile /var/run/sslh/sslh.pid&nbsp;›  
$ declare -A sslh\_infos  
$ for i in $awk\_output

> do  
> [[«&nbsp;$i&nbsp;» =~ ^--]] && index=«&nbsp;${i##\*-}&nbsp;» || sslh\_infos[«&nbsp;$index&nbsp;»]=«&nbsp;$i&nbsp;»  
> done  
> $  
> $ for i in «&nbsp;${!sslh\_infos[@]}&nbsp;»; do echo «&nbsp;$i : ${sslh\_infos[&nbsp;»$i"]}"; done  
> pidfile : /var/run/sslh/sslh.pid  
> user : sslh  
> ssl : 127.0.0.1:443  
> ssh : 127.0.0.1:247  
> listen : 192.168.147.3:443[/code]ça marchera tant que les paramètres des options ne contiendront pas d’espaces. 😕

---

<div class="post-metadata">

**Author:** ![PengouinPdt](https://www.debian-fr.org/user_avatar/www.debian-fr.org/pengouinpdt/32/3978_2.png) [@PengouinPdt](https://www.debian-fr.org/u/PengouinPdt)\
**Post date:** [Mars 31, 2016, 4:50 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/5 "2016-03-31T04:50:56Z")

</div>

> [@Watael](#):
>
> ça marchera tant que les paramètres des options ne contiendront pas d’espaces.

On est tout à fait d’accord.  
C’est même le propos. L’auteur de SSLH l’a voulu ainsi ; un jour cela pourra peut-être changé.

Merci pour ton code. C’est de la concision, çà ! Bien…

---

<div class="post-metadata">

**Author:** ![PengouinPdt](https://www.debian-fr.org/user_avatar/www.debian-fr.org/pengouinpdt/32/3978_2.png) [@PengouinPdt](https://www.debian-fr.org/u/PengouinPdt)\
**Post date:** [Mars 31, 2016, 6:32 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/6 "2016-03-31T06:32:32Z")

</div>

En effet, ça fonctionne !

```auto
cmd="$(awk -F '"' '/DAEMON_OPTS/ { print $2 }' /etc/default/sslh)"""

for i in ${cmd// / }; do
    [["$i" =~ ^--]] && index="${i##*-}" || SSLH_INFOS["${index}"]="$i"
done
```

Maintenant, si je fais :

```auto
for i in $(awk -F '"' '/DAEMON_OPTS/ { print $2 }' /etc/default/sslh); do 
    [["$i" =~ ^--]] && index="${i##*-}" || SSLH_INFOS["${index}"]="$i"
done
```

Shellcheck m’informe de cette erreur [SC2013](https://github.com/koalaman/shellcheck/wiki/SC2013 "To read lines rather than words, pipe/redirect to a 'while read' loop.") : de préférer à l’écriture ci-dessus, l’usage d’une boucle while …

Ça tombe bien, j’aime les boucles while :p, sauf que si j’écris ça :

```auto
while IFS=" " read -r i ; do
    echo "i: $i"
    [["$i" =~ ^--]] && index="${i##*-}" || SSLH_INFOS["${index}"]="$i"
done < <(awk -F '"' '/DAEMON_OPTS/ { print $2 }' /etc/default/sslh)
```

Cela ne fonctionne pas, parce que - logiquement - cela retourne ligne par ligne, dans son ensemble - comme il n’y en a qu’une, cela affecte à i l’ensemble de la ligne.  
Hors, j’aurais cru que l’emploi de la variable IFS, filtrant sur une espace aurait fonctionné … et, bien non !

* * *

Après il est possible d’écrire ainsi :

```auto
while IFS=" " read -r var1 val1 var2 val2 var3 val3 ; do
```

Mais sincèrement ca devient trop complexe, à la fois pour l’usage que j’en ai … et puis si le projet SSLH est utilisé pour filtrer aussi du VPN, voire autre flux qu’il est capable de multiplexer, et donc paramétré, c’est dans cette écriture vite ingérable !

* * *

Bref, cela m’aura permis de réviser un peu mon Bash : [http://mywiki.wooledge.org/BashFAQ/001](http://mywiki.wooledge.org/BashFAQ/001)  
Et, de voir certaines écritures intéressantes, telle que la 6ème pour obtenir les infos à partir du fichier /etc/passwd. 😃

---

<div class="post-metadata">

**Author:** ![Watael](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/w/a4c791/32.png) [@Watael](https://www.debian-fr.org/u/Watael)\
**Post date:** [Mars 31, 2016, 6:47 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/7 "2016-03-31T06:47:02Z")

</div>

normalement, on ne boucle pas avec _for_ sur une substitution de commandes, parce que ça coupe les lignes en mots.  
mais, ici, c’est ce qu’on veut faire : boucler sur chaque mot !

si tu tiens à le faire avec une boucle while, c’est le _delimiter_ de la commande read qu’il faut assigner à _espace_.

`$ phrase='shellcheck est un automate imbécile'
$ while read -d ' ' w; do echo "$w"; done <<<"$phrase"; echo "$w" #sinon il manquera un mot ! comme quoi, ce n'est peut-être pas la meilleure méthode.
shellcheck
est
un
automate
imbécile
$`

---

<div class="post-metadata">

**Author:** ![PengouinPdt](https://www.debian-fr.org/user_avatar/www.debian-fr.org/pengouinpdt/32/3978_2.png) [@PengouinPdt](https://www.debian-fr.org/u/PengouinPdt)\
**Post date:** [Mars 31, 2016, 6:53 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/8 "2016-03-31T06:53:26Z")

</div>

Je te remercie profondèment de ton intervention ; en effet, ce code fonctionne correctement :

```auto
while read -r -d ' ' world; do
    [["${world}" =~ ^--]] && index="${world##*-}" || SSLH_INFOS["${index}"]="${world}"
done < <(awk -F '"' '/DAEMON_OPTS/ { print $2 }' /etc/default/sslh)
```

👏

* * *

Maintenant, à savoir ce qui est plus efficace, efficient, entre la boucle find que tu as écrite, et ce while …  
Faut que je me renseigne là-dessus, moi !

- Ma première boucle find: real 0m0.084s
- Pour la while: real 0m0.021s
- Pour la find que tu nous a montré : real 0m0.016s

Et, quand même … 🌬  
Comme quoi, les choses les plus simples sont les meilleurs !

---

<div class="post-metadata">

**Author:** ![ripat](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/r/bc79bd/32.png) [@ripat](https://www.debian-fr.org/u/ripat)\
**Post date:** [Mars 31, 2016, 9:00 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/9 "2016-03-31T09:00:20Z")

</div>

Sinon, pour les amateurs de _one liner_, il y avait aussi ceci qui comprend l’extraction de la ligne du fichier `/etc/default/sslh`:

```auto
# one liner
#
eval "declare -A sslh_info=("$(awk 'BEGIN{FS="\"?--|\""}/^DAEMON_OPTS/{for(i=2;i<NF;i++){split($i,a," "); printf "[%s]=%s ", a[1],a[2]}}' /etc/default/sslh)")"
#
# vérification
#
for i in "${!sslh_info[@]}"; do printf "%-10s%s %s\n" "$i" ":" "${sslh_info[$i]}";done
#
# retourne:
#
pidfile : /var/run/sslh/sslh.pid
user : sslh
ssl : 127.0.0.1:443
ssh : 127.0.0.1:247
listen : 192.168.147.3:443
```

Exercice de style peu lisible, je vous l’accorde.

**EDIT**  
_one liner_ courte encore ( et sans eval. eval=evil!):

~~declare $(awk ‘BEGIN{FS=""?–|""}/^DAEMON\_OPTS/{for(i=2;i\<NF;i++){split($i,a," "); print "sslh\_info[“a[1]”]="a[2]}}’ /etc/default/sslh)~~

```auto
declare -A $(awk 'BEGIN{FS="\"?--|\""}/^DAEMON_OPTS/{for(i=2;i<NF;i++){split($i,a," "); print "sslh_info["a[1]"]="a[2]}}' /etc/default/sslh)

```

---

<div class="post-metadata">

**Author:** ![PengouinPdt](https://www.debian-fr.org/user_avatar/www.debian-fr.org/pengouinpdt/32/3978_2.png) [@PengouinPdt](https://www.debian-fr.org/u/PengouinPdt)\
**Post date:** [Mars 31, 2016, 9:40 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/10 "2016-03-31T09:40:21Z")

</div>

> [@ripat](#):
>
> one liner courte encore ( et sans eval. eval=evil!):

Ahhh, tu me rassures ! 😛  
C’est pour cette raison que j’ai mis 😍 \<3, après ta correction. 😉

* * *

Temps d’exécution :

> real 0m0.017s

---

<div class="post-metadata">

**Author:** ![ripat](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/r/bc79bd/32.png) [@ripat](https://www.debian-fr.org/u/ripat)\
**Post date:** [Mars 31, 2016, 9:43 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/11 "2016-03-31T09:43:57Z")

</div>

Et bien non, mon edit plus haut ne marche pas pour les array associatives. Je corrige mon post. Intégrer du bash dans du awk est toujours pénible. J’essaye, en général de tout faire en awk quand c’est possible.

---

<div class="post-metadata">

**Author:** ![PengouinPdt](https://www.debian-fr.org/user_avatar/www.debian-fr.org/pengouinpdt/32/3978_2.png) [@PengouinPdt](https://www.debian-fr.org/u/PengouinPdt)\
**Post date:** [Mars 31, 2016, 9:45 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/12 "2016-03-31T09:45:02Z")

</div>

Moi aussi de plus en plus, j’aime les possibilités qu’il offre, même si je suis très loin de le maîtriser !

---

<div class="post-metadata">

**Author:** ![ripat](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/r/bc79bd/32.png) [@ripat](https://www.debian-fr.org/u/ripat)\
**Post date:** [Mars 31, 2016, 9:52 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/13 "2016-03-31T09:52:45Z")

</div>

Voilà j’ai corrigé mon edit plus haut. Ça devrait marcher maintenant (et sans eval!).

---

<div class="post-metadata">

**Author:** ![seb-ksl](https://www.debian-fr.org/user_avatar/www.debian-fr.org/seb-ksl/32/2946_2.png) [@seb-ksl](https://www.debian-fr.org/u/seb-ksl)\
**Post date:** [Mars 31, 2016, 11:13 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/14 "2016-03-31T11:13:35Z")

</div>

> [@ripat](#):
>
> declare -A $(awk ‹&nbsp;BEGIN{FS=«&nbsp;"?–|"&nbsp;»}/^DAEMON\_OPTS/{for(i=2;i\<NF;i++){split($i,a," "); print "sslh\_info[«&nbsp;a[1]&nbsp;»]="a[2]}}&nbsp;› /etc/default/sslh)

> [@ripat](#):
>
> Exercice de style peu lisible, je vous l’accorde.

J’ai vomi. Et pas parce que je suis content ☹.

---

<div class="post-metadata">

**Author:** ![PengouinPdt](https://www.debian-fr.org/user_avatar/www.debian-fr.org/pengouinpdt/32/3978_2.png) [@PengouinPdt](https://www.debian-fr.org/u/PengouinPdt)\
**Post date:** [Mars 31, 2016, 11:19 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/15 "2016-03-31T11:19:11Z")

</div>

> [@seb-ksl](#):
>
> J’ai vomi.

T’es un peu dur, là, non ?  
Faut avouer l’exercice de style est là, quand même …

Bon, après je ne comprends pas comment ça fait pour attribuer au tableau associatif.  
Mais, c’est sidérant ce qu’on peut faire.

* * *

Allez, «&nbsp;miam-time&nbsp;». Bon app, all

---

<div class="post-metadata">

**Author:** ![seb-ksl](https://www.debian-fr.org/user_avatar/www.debian-fr.org/seb-ksl/32/2946_2.png) [@seb-ksl](https://www.debian-fr.org/u/seb-ksl)\
**Post date:** [Mars 31, 2016, 11:32 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/16 "2016-03-31T11:32:49Z")

</div>

> [@PengouinPdt](#):
>
> T’es un peu dur, là, non ?Faut avouer l’exercice de style est là, quand même …

C’était juste une boutade mais OK, je développe 🙂.  
Oui c’est effectivement un exercice de style. C’est simplement que je souscris nettement plus à la philosophie qui dit que le code est fait **d’abord** pour être lu par des humains, **puis** pour être exécuté par une machine. Et ce genre de one-liners est diamétralement opposé. C’est plutôt la philosophie du «&nbsp;tant que ça marche…&nbsp;».

---

<div class="post-metadata">

**Author:** ![ripat](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/r/bc79bd/32.png) [@ripat](https://www.debian-fr.org/u/ripat)\
**Post date:** [Mars 31, 2016, 12:13 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/17 "2016-03-31T12:13:43Z")

</div>

En plus lisible (mais, faut un peu connaître awk évidemment)

```auto
#!/bin/bash

declare -A $(awk '
  BEGIN{
    FS="\"?--|\""
  }
  /^DAEMON_OPTS/{
    for(i=2;i<NF;i++){
      split($i,a," ")
      print "sslh_info["a[1]"]="a[2]
      }
  }' /etc/default/sslh
)
#
# Check
for i in "${!sslh_info[@]}"; do
  printf "%-10s%s %s\n" "$i" ":" "${sslh_info[$i]}"
done;
unset sslh_info

```

Ce code est valide, efficace et évite un certain nombre de sub-shell (process) comme avec ce que j’ai vu plus haut `awk | awk | grep | traitement bash`

Est-ce que la notation bash `[condition] && code si oui || code sinon` te rend malade aussi?

---

<div class="post-metadata">

**Author:** ![PengouinPdt](https://www.debian-fr.org/user_avatar/www.debian-fr.org/pengouinpdt/32/3978_2.png) [@PengouinPdt](https://www.debian-fr.org/u/PengouinPdt)\
**Post date:** [Mars 31, 2016, 12:39 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/18 "2016-03-31T12:39:59Z")

</div>

Arrête de faire mumuse, et «&nbsp;ne vous battez pas les nenfants&nbsp;» 😉

> [@ripat](#):
>
> Est-ce que la notation bash [condition] && code si oui || code sinon te rend malade aussi?

Perso : non.  
Même s’il me semble avoir lu, dans un de mes books sur Bash, qu’il vaut mieux éviter pour faciliter la compréhension, la lecture.  
Perso, je m’y suis fait 😉

Ensuite, étant donné que le temps de traitement est sensiblement le même que la boucle find écrite par @Watael - je préfère car, elle je la comprends.

J’arrive à lire ton instruction oneliner , mais je ne comprends pas comment il attribue le tableau associatif, le construit, puis lui affecte ses valeurs … alors que je comprends la boucle for, l’instruction split, ainsi que le BEGIN - celui-ci, je n’aurais pas eu l’idée de l’écrire ainsi. Et, pourquoi tu démarres la variable i à 2 ?!

Donc, pour moi, oui, c’est puissant, je trouve cela «&nbsp;beau&nbsp;» qu’awk soit capable de faire un tel traitement, mais si je ne le comprends pas pleinement, ça m’embête. De fait, je vais avoir tendance à utiliser quelque chose que je comprends, qui m’es clair.  
Même si - _pour l’exemple_ - pendant longtemps, l’instruction if … else abrégé m’a posé soucis. Ce n’est plus un problème … à force de coder. Donc, je ne me soucis pas, il y aura un temps, où je serais capable de comprendre ton code oneliner. 😛

Autre point : ton code oneliner créé le tableau associatif … soit, mais si le tableau est déclaré auparavant dans un autre fichier, c’est d’ailleurs dans cette idée que je l’ai créé en première instruction.  
Comment tu résous le problème ?

---

<div class="post-metadata">

**Author:** ![Watael](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/w/a4c791/32.png) [@Watael](https://www.debian-fr.org/u/Watael)\
**Post date:** [Mars 31, 2016, 12:54 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/19 "2016-03-31T12:54:48Z")

</div>

[quote=“seb-ksl, post:16, topic:68927”]  
C’est simplement que je souscris nettement plus à la philosophie qui dit que le code est fait d’abord pour être lu par des humains[…][/quote]

à ce compte là, on n’est pas près de te voir coder en sed.

---

<div class="post-metadata">

**Author:** ![ripat](https://www.debian-fr.org/letter_avatar_proxy/v4/letter/r/bc79bd/32.png) [@ripat](https://www.debian-fr.org/u/ripat)\
**Post date:** [Mars 31, 2016, 1:21 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/20 "2016-03-31T13:21:27Z")

</div>

> [@PengouinPdt](#):
>
> Ensuite, étant donné que le temps de traitement est sensiblement le même que la boucle find écrite par @Watael - je préfère car, elle je la comprends.

C’est ce qui compte car finalement c’est toi qui va devoir en faire la maintenance. Cela dit, rien ne t’empêche d’optimiser en supprimant les sous-process (pipe inutiles, awk puis grep - ou l’inverse - sans oublier le fameux [UUOC](http://porkmail.org/era/unix/award.html) etc… Le one liner que je propose évite bon nombre de ces problèmes.

> [@PengouinPdt](#):
>
> mais je ne comprends pas comment il attribue le tableau associatif, le construit, puis lui affecte ses valeurs

Le code awk imprime simplement ceci:  
`sslh_info[user]=sslh sslh_info[listen]=192.168.147.3:443 sslh_info[ssh]=127.0.0.1:247 sslh_info[ssl]=127.0.0.1:443`

L’instruction `declare -A $(code awk)` revient à faire:  
`declare -A sslh_info[user]=sslh sslh_info[listen]=192.168.147.3:443 sslh_info[ssh]=127.0.0.1:247 sslh_info[ssl]=127.0.0.1:443`

Enfin, mon code «&nbsp;aéré&nbsp;» reste un one liner mais sur plusieurs lignes pour faciliter la lecture. One liner ne veut pas nécessairement dire [obfuscated code](https://en.wikipedia.org/wiki/Obfuscation_(software))

---

<div class="post-metadata">

**Author:** ![seb-ksl](https://www.debian-fr.org/user_avatar/www.debian-fr.org/seb-ksl/32/2946_2.png) [@seb-ksl](https://www.debian-fr.org/u/seb-ksl)\
**Post date:** [Mars 31, 2016, 1:30 UTC](https://www.debian-fr.org/t/bash-awk-variable-dans-array/68927/21 "2016-03-31T13:30:46Z")

</div>

> [@Watael](#):
>
> à ce compte là, on n’est pas près de te voir coder en sed.

Effectivement, pas plus qu’en Perl ;-).
