277 votes

Comment puis-je analyser un fichier YAML à partir d'un script shell Linux ?

Je souhaite fournir un fichier de configuration structuré, aussi simple que possible à modifier pour un utilisateur non technique (malheureusement, cela doit être un fichier) et donc j'ai voulu utiliser YAML. Je ne trouve aucun moyen de le parser à partir d'un script shell Unix cependant.

0 votes

Pas directement votre question, mais vous voudrez peut-être jeter un œil à ansible si votre script shell concerne particulièrement la gestion à distance de différents nœuds (et un inventaire yaml)

20 votes

Essayez d'utiliser yq pour lire/écrire des fichiers yaml dans le shell. La page du projet est ici: mikefarah.github.io/yq Vous pouvez installer l'outil avec brew, apt ou télécharger le binaire. Lire une valeur est aussi simple que yq r some.yaml key.value

0 votes

@kenorb JSON != yml/YAML

365voto

Stefan Farestam Points 431

Voici un analyseur basé sur bash qui utilise sed et awk pour analyser des fichiers yaml simples :

function parse_yaml {
   local prefix=$2
   local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
   sed -ne "s|^\($s\):|\1|" \
        -e "s|^\($s\)\($w\)$s:$s[\"']\(.*\)[\"']$s\$|\1$fs\2$fs\3|p" \
        -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p"  $1 |
   awk -F$fs '{
      indent = length($1)/2;
      vname[indent] = $2;
      for (i in vname) {if (i > indent) {delete vname[i]}}
      if (length($3) > 0) {
         vn=""; for (i=0; i

`Il comprend des fichiers tels que :

## définitions globales
global:
  debug: yes
  verbose: no
  debugging:
    detailed: no
    header: "debugging started"

## sortie
output:
   file: "yes"

Qui, lorsqu'il est analysé avec :

parse_yaml sample.yml

donnera en sortie :

global_debug="yes"
global_verbose="no"
global_debugging_detailed="no"
global_debugging_header="debugging started"
output_file="yes"

Il comprend également des fichiers yaml générés par ruby qui peuvent inclure des symboles ruby, comme :

---
:global:
  :debug: 'yes'
  :verbose: 'no'
  :debugging:
    :detailed: 'no'
    :header: debugging started
  :output: 'yes'

et donnera le même résultat que dans l'exemple précédent.

Une utilisation classique dans un script est la suivante :

eval $(parse_yaml sample.yml)

parse_yaml accepte un argument de préfixe afin que toutes les configurations importées aient un préfixe commun (ce qui réduira les risques de collisions de noms).

parse_yaml sample.yml "CONF_"

donne :

CONF_global_debug="yes"
CONF_global_verbose="no"
CONF_global_debugging_detailed="no"
CONF_global_debugging_header="debugging started"
CONF_output_file="yes"

Remarquez que les configurations précédentes dans un fichier peuvent être référencées par les configurations ultérieures :

## définitions globales
global:
  debug: yes
  verbose: no
  debugging:
    detailed: no
    header: "debugging started"

## sortie
output:
   debug: $global_debug

Une autre utilisation intéressante est de d'abord analyser un fichier de valeurs par défaut puis les paramètres de l'utilisateur, ce qui fonctionne puisque les paramètres ultérieurs remplacent les premiers :

eval $(parse_yaml defaults.yml)
eval $(parse_yaml project.yml)`

5 votes

Cool Stefan! Ce serait incroyable si cela pouvait transformer la notation yaml - en tableaux bash natifs aussi!

3 votes

Cela devrait être assez facile à faire si vous modifiez la ligne printf dans le script awk. Notez cependant que bash ne prend pas en charge les tableaux associatifs multidimensionnels, vous vous retrouvez donc avec un tableau + une seule clé par valeur. Hmm, il faudrait probablement déplacer cela vers github...

5 votes

Cela attend l'indentation yml standard de 2 espaces. Si vous utilisez 4 espaces, alors les variables auront deux underscores comme délimiteur, par exemple global__debug au lieu de global_debug .

121voto

vaab Points 1639

J'ai écrit shyaml en python pour les besoins de requête YAML à partir de la ligne de commande du shell.

Aperçu:

$ pip install shyaml      ## installation

Fichier YAML d'exemple (avec des fonctionnalités complexes):

$ cat < test.yaml
name: "MonNom !!"
subvalue:
    how-much: 1.1
    things:
        - first
        - second
        - third
    other-things: [a, b, c]
    maintainer: "Valentin Lab"
    description: |
        Description multiline:
        Ligne 1
        Ligne 2
EOF

Requête de base:

$ cat test.yaml | shyaml get-value subvalue.maintainer
Valentin Lab

Requête de boucle plus complexe sur des valeurs complexes:

$ cat test.yaml | shyaml values-0 | \
  while read -r -d $'\0' value; do
      echo "RECEIVED: '$value'"
  done
RECEIVED: '1.1'
RECEIVED: '- first
- second
- third'
RECEIVED: '2'
RECEIVED: 'Valentin Lab'
RECEIVED: 'Description multiline:
Ligne 1
Ligne 2'

Quelques points clés:

  • tous les types YAML et les bizarreries de syntaxe sont correctement gérés, comme les chaînes multilignes, les chaînes entre guillemets, les séquences inline...
  • la sortie rembourrée avec \0 est disponible pour une manipulation solide de l'entrée multiligne.
  • la notation pointée simple pour sélectionner les sous-valeurs (par ex.: subvalue.maintainer est une clé valide).
  • l'accès par index est fourni pour les séquences (par ex.: subvalue.things.-1 est le dernier élément de la séquence subvalue.things.)
  • accès à tous les éléments de séquences/structures en une seule fois pour une utilisation dans des boucles bash.
  • vous pouvez afficher une partie entière d'un fichier YAML en ... YAML, ce qui se marie bien pour des manipulations ultérieures avec shyaml.

Plus d'exemples et de documentation sont disponibles sur la page github de shyaml ou la page PyPI de shyaml.

1 votes

C'est impressionnant! Ce serait génial s'il y avait un indicateur pour ignorer les valeurs yaml qui sont vides dans la sortie. En ce moment, il affiche "null". Je l'utilise avec envdir pour produire un fichier docker-compose dans envdir cat docker-compose.yml | shyaml get-value api.environment | grep -v null | awk -F': ' '{print $2 > ("envdir/" $1)}'

0 votes

@JiminyCricket Veuillez utiliser la page des problèmes GitHub ! Je serais ravi de pouvoir au moins suivre cela. ;)

1 votes

Malheureusement, shyaml est ridiculement lent.

75voto

curtisblackwell Points 446

Mon cas d'utilisation peut être ou non tout à fait le même que ce que demandait ce post original, mais c'est définitivement similaire.

J'ai besoin d'extraire du YAML sous forme de variables bash. Le YAML ne dépassera jamais un niveau de profondeur.

Le YAML ressemble à ceci :

KEY:                valeur
ANOTHER_KEY:        autre_valeur
OH_MY_SO_MANY_KEYS: encore_une_autre_valeur
LAST_KEY:           dernière_valeur

La sortie ressemblerait à ceci :

KEY="valeur"
ANOTHER_KEY="autre_valeur"
OH_MY_SO_MANY_KEYS="encore_une_autre_valeur"
LAST_KEY="dernière_valeur"

J'ai obtenu la sortie avec cette ligne :

sed -e 's/:[^:\/\/]/="/g;s/$/"/g;s/ *=/=/g' fichier.yaml > fichier.sh
  • s/:[^:\/\/]/="/g trouve : et le remplace par =", en ignorant :// (pour les URL)
  • s/$/"/g ajoute " à la fin de chaque ligne
  • s/ *=/=/g supprime tous les espaces avant =

13 votes

Pas sûr de ce que vous voulez dire, mais si vous voulez dire que cela ne fonctionne pas pour tous les YAML, vous avez raison. C'est pourquoi j'ai commencé par quelques qualifications. J'ai juste partagé ce qui a fonctionné pour mon cas d'utilisation, car cela répondait mieux à la question que tout autre à ce moment-là. Cela peut certainement être développé.

4 votes

Un peu ouvert à l'injection de code aussi, mais comme vous l'avez dit, c'est un pas en avant

1 votes

Je n'ai jamais écrit que des scripts shell à utiliser localement, donc je n'ai pas eu à m'en soucier. Cependant, si vous savez comment le sécuriser et/ou si vous souhaitez développer, je vous en serais vraiment reconnaissant.

34voto

Rafael Points 320

Il est possible de passer un petit script à certains interprètes, comme Python. Un moyen facile de le faire en utilisant Ruby et sa bibliothèque YAML est le suivant :

$ RUBY_SCRIPT="data = YAML::load(STDIN.read); puts data['a']; puts data['b']"
$ echo -e '---\na: 1234\nb: 4321' | ruby -ryaml -e "$RUBY_SCRIPT"
1234
4321

, où data est un hash (ou tableau) avec les valeurs de yaml.

En bonus, cela analysera parfaitement le front matter de Jekyll.

ruby -ryaml -e "puts YAML::load(open(ARGV.first).read)['tags']" example.md

1 votes

Est-ce utilisable? Vous avez mis yaml par echo à l'interpréteur ruby. mais comment utiliser cette variable dans le reste du script bash?

0 votes

Oui, c'est utilisable. La variable RUBY_SCRIPT est un script ruby qui peut être écrit dans un fichier à la place (exécuté avec ruby -ryaml ). Il contient la logique pour transformer le texte d'entrée en un texte de sortie, stockant internalement le contenu dans la variable data. L'écho produit un texte yaml, mais vous pouvez utiliser cat pour rediriger le contenu d'un fichier à la place.

0 votes

Je suis désolé mais je ne vois pas cela dans l'exemple ci-dessus. Tout d'abord, la variable RUBY_SCRIPT contient le code pour l'interpréteur ruby. Ensuite, echo -e simule des données yaml, ces dernières sont redirigées dans l'interpréteur ruby. Cela appelle le code ruby en tant que script en ligne et enfin imprime les exemples de sortie des variables 'a' et 'b'. Ensuite, où est le chargement de la variable dans bash pour son reste de code exécutable? Je ne vois qu'une solution de contournement : mettre la sortie de ruby dans un fichier temporaire, qui devrait contenir les lignes : variable = 'valeur', et après charger ces données dans bash avec '.fichier_temporaire'. Mais ce n'est qu'une solution temporaire, pas une résolution.

11voto

dogbane Points 85749

Difficile à dire car cela dépend de ce que vous voulez que l'analyseur extrait de votre document YAML. Pour des cas simples, vous pourriez utiliser grep, cut, awk, etc. Pour une analyse plus complexe, vous auriez besoin d'utiliser une bibliothèque d'analyse complète telle que PyYAML de Python ou YAML::Perl.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X