JC, convertit la sortie d’outils en ligne de commande populaires en JSON
(github.com/kellyjonbrazil)- JC est un outil qui convertit en JSON la sortie de nombreux outils CLI, formats de fichiers et chaînes génériques afin de faciliter leur parsing dans des scripts
- Il accepte une entrée via pipe et produit du JSON sur
STDOUT, tout en prenant aussi en charge une magic syntax en le plaçant devant une commande, comme dansjc dig example.com - La sortie par défaut utilise un schéma strict propre à chaque parseur et convertit les nombres,
null, les booléens ainsi que des champs sémantiques supplémentaires ; on peut accéder au JSON brut avant prétraitement avec-rouraw=True - Il peut aussi être utilisé comme bibliothèque Python, ce qui permet de recevoir le résultat sous forme de dictionnaire Python, de liste de dictionnaires ou d’iterable paresseux au lieu de JSON
- Pour les pipelines volumineux ou de longue durée, il propose des parseurs en streaming exportant en JSON Lines/NDJSON, ainsi que
--slurp,--meta-out, des plugins de parseur locaux et des options pour supprimer les avertissements de compatibilité plateforme
Ce que fait JC
- JC(JSON Convert) convertit en JSON la sortie de nombreux outils CLI, formats de fichiers et chaînes génériques
- La sortie convertie peut ensuite être envoyée par pipe vers des outils comme
jqoujellopour un traitement supplémentaire - Un exemple est
dig example.com | jc --dig, qui convertit la sortie dedigen tableau JSON, puisjq -r '.[].answer[].data'pour n’extraire que les adresses IP - La même opération peut aussi être exécutée avec la magic syntax, comme
jc dig example.com | jq -r '.[].answer[].data'
Utilisation en CLI et comme bibliothèque Python
- L’usage CLI de base consiste à recevoir une entrée via pipe et à produire une représentation JSON
COMMAND | jc [SLICE] [OPTIONS] PARSERcat FILE | jc [SLICE] [OPTIONS] PARSERecho STRING | jc [SLICE] [OPTIONS] PARSER
- La magic syntax consiste à préfixer une commande ou un fichier
/procavecjc, sous la formejc [SLICE] [OPTIONS] COMMANDoujc [SLICE] [OPTIONS] /proc/<path-to-procfile>- Les alias de commande et les shell builtins ne sont pas pris en charge
- En Python, on l’appelle par exemple avec
jc.parse('dig', cmd_output)- La valeur de retour n’est pas une chaîne JSON mais peut être un dictionnaire Python, une liste de dictionnaires ou, pour les parseurs en streaming, un iterable paresseux
- La documentation du package Python est disponible via
help('jc'),help('jc.lib')ou dans la documentation en ligne
Représentation de la sortie et schéma
- La représentation par défaut utilise un schéma strict propre à chaque parseur
- Les nombres connus sont convertis en valeurs JSON
intoufloat - Les valeurs
Noneconnues sont converties en JSONnull - Les valeurs booléennes connues sont également converties
- Certains parseurs ajoutent des champs sémantiques supplémentaires
- Les nombres connus sont convertis en valeurs JSON
- Le JSON brut avant prétraitement est accessible via l’option
-ren CLI ouparse(..., raw=True)dans la bibliothèque Python - Le schéma de chaque parseur peut être consulté via les liens de documentation figurant à côté de la liste des parseurs
- La sortie JSON est compacte par défaut, et l’option
-ppermet d’utiliser un pretty format - Une sortie YAML est aussi possible avec
-you--yaml-out
Parseurs pris en charge et exemples d’usage
- Les éléments pris en charge se répartissent entre commandes CLI, formats de fichiers et parseurs de chaînes
- La liste des parseurs dans le README inclut notamment
dig,ls,ping,ps,netstat,ifconfig,csv,xml,yaml,/etc/hosts,/etc/passwd,/proc/,systemctl,git log,jwt,url,semveret des fichiers liés àx509 - Les exemples de sortie montrent comment divers types d’entrée sont transformés en structures JSON
- La sortie de
arpest convertie en champs adresse, type matériel, adresse MAC et interface - Un fichier CSV est converti en tableau d’objets utilisant les en-têtes comme clés
/etc/hostsest converti en adresses IP et tableaux de noms d’hôteifconfigest converti en objet contenant le nom de l’interface, la MTU, les adresses IPv4/IPv6, les compteurs de paquets et d’octets, etc.pingest converti en objet contenant les nombres de paquets envoyés/reçus, le taux de perte, le temps aller-retour et la liste des réponses
- La sortie de
- Dans Ansible, il peut être utilisé comme filter plugin de la collection
community.general
Installation
jcpeut être installé de plusieurs façonspip3 install jc- via les dépôts de paquets de l’OS
- via des binaires par architecture sur GitHub Releases
- Exemples d’installation via les paquets de l’OS
- Debian/Ubuntu :
apt-get install jc - Fedora :
dnf install jc - openSUSE :
zypper install jc - Arch :
pacman -S jc - macOS :
brew install jc - FreeBSD : installation via les ports
- Filter plugin Ansible :
ansible-galaxy collection install community.general - Connecteur FortiSOAR : installation depuis le FortiSOAR Connector Marketplace
- Debian/Ubuntu :
Options principales
-a/--about: affiche des informations surjcet ses parseurs en JSON ou YAML-d/--debug: affiche des messages de trace en cas de problème de parsing ;-ddfournit un niveau de debug plus détaillé-h/--help: affiche l’aide, etjc -h --parser_namepermet de consulter la documentation d’un parseur-M/--meta-out: ajoute à la sortie des métadonnées comme l’horodatage, le nom du parseur, la magic command et le code de sortie de la magic command-q/--quiet: supprime les avertissements des parseurs ;-qqignore les erreurs des parseurs en streaming-s/--slurp: regroupe plusieurs lignes d’entrée dans un tableau-u/--unbuffer: désactive la mise en tampon de la sortie-B/--bash-comp,-Z/--zsh-comp: génère un script de complétion Bash ou Zsh
Slice et Slurp
- Slice traite seulement une partie des lignes d’entrée avec une syntaxe
START:STOPsemblable au slicing Python - Par exemple, pour un tableau avec en-tête et pied de page,
jc 1:-1 --asciitableoujc 1:4 --asciitablepermet de convertir uniquement les données intermédiaires en JSON - Les slices positives et les slices vides sont les plus économes en mémoire, tandis que les slices négatives utilisent davantage de mémoire
- Slurp permet à des parseurs de chaînes acceptant une seule ligne d’entrée de produire en une fois un tableau contenant plusieurs éléments
- Exemple : un fichier contenant une adresse IP par ligne peut être traité avec
jc --slurp --ip-address
- Exemple : un fichier contenant une adresse IP par ligne peut être traité avec
- La magic syntax
/procprend automatiquement en charge le slurp quand plusieurs fichiers sont sélectionnés- Lors de la conversion de plusieurs fichiers
/proc, un champ_fileest ajouté à chaque objet résultat pour indiquer de quel fichier il provient
- Lors de la conversion de plusieurs fichiers
- Si
--meta-outest utilisé avec slurp, la sortie adopte une structure enveloppée avec un tableauresultet un objet_jc_meta
Codes de sortie et métadonnées
- Une erreur fatale interne à
jcproduit le code de sortie100, sinon il renvoie0 - En cas d’usage de la magic syntax,
jcconserve le code de sortie du programme analysé et l’ajoute à son propre code de sortie- Si
ifconfigvaut1etjcvaut0, le code combiné vaut1 - Si
ifconfigvaut0etjcvaut100, le code combiné vaut100 - Si les deux sont en erreur, le code combiné vaut
101
- Si
- Avec
--meta-outou-Men magic syntax, l’objet_jc_metainclut les informations sur la magic command et son code de sortie
Couleurs et variables d’environnement
- La variable d’environnement
JC_COLORSpermet de définir les couleurs des key names, keywords, nombres et chaînes- Le format est
JC_COLORS=<keyname_color>,<keyword_color>,<number_color>,<string_color> - Les couleurs possibles sont
black,red,green,yellow,blue,magenta,cyan,gray,brightblack,brightred,brightgreen,brightyellow,brightblue,brightmagenta,brightcyan,white,default
- Le format est
- Si la variable d’environnement
NO_COLORest définie, la sortie colorée est désactivée - L’option
-Cforce la sortie colorée et est prioritaire sur la variable d’environnementNO_COLORet sur l’option-m
Parseurs en streaming
- La plupart des parseurs lisent l’intégralité de
STDINen mémoire avant de parser puis de sérialiser un document JSON en sortie - Certains parseurs en streaming traitent l’entrée ligne par ligne dès sa réception et produisent du JSON Lines ou du NDJSON
- Exemples :
ls-s,ping-s - Cela peut réduire fortement l’usage mémoire sur de grosses sorties de commandes comme
ls -lR /et, dans certains cas, accélérer le traitement
- Exemples :
- Les parseurs en streaming ne peuvent pas être utilisés avec la magic syntax
- Pour éviter qu’une erreur de parsing ne casse un pipeline de longue durée, on peut utiliser
-qqouignore_exceptions=Trueen Python- Les lignes réussies incluent
_jc_meta.success: true - Les lignes en échec incluent
_jc_meta.success: false,erroretline
- Les lignes réussies incluent
- Si la sortie tarde à apparaître entre deux pipes à cause des buffers du système d’exploitation, on peut utiliser
-upour une sortie non tamponnée- Toutefois, sur de gros flux de données, la sortie non tamponnée peut être plus lente
- En Python, les parseurs en streaming acceptent une entrée iterable et renvoient un objet iterable, ce qui permet un traitement paresseux
Plugins de parseur
- Les parser plugins locaux peuvent être placés dans le dossier
jc/jcparsersdu répertoire de données de l’application- Linux/unix :
$HOME/.local/share/jc/jcparsers - macOS :
$HOME/Library/Application Support/jc/jcparsers - Windows :
$LOCALAPPDATA\jc\jc\jcparsers
- Linux/unix :
- Un plugin est un fichier de module Python standard
- On peut utiliser
jc/parsers/foo.pyoujc/parsers/foo_s.pycomme modèle - Le nom de fichier du plugin doit être un nom de module Python valide : commencer par une lettre et ne contenir que des caractères alphanumériques et des underscores
- Un plugin local peut remplacer un parseur par défaut
Locale, fuseau horaire et compatibilité
- Pour de meilleurs résultats, il est recommandé de définir
LC_ALLsurCouen_US.UTF-8 - Sur certains anciens systèmes, la locale
Cne prend pas en charge l’encodage UTF-8, ce qui peut dégrader la sortie UTF-8 en ASCII avec des séquences d’échappement\\u - Certains parseurs ajoutent des champs d’horodatage epoch calculés
- Si le nom du champ n’a pas le suffixe
_utc, il est considéré comme un horodatage naïf basé sur le fuseau horaire local - Si le fuseau UTC est détecté dans le texte de sortie de la commande, l’horodatage devient timezone aware et le nom de clé reçoit le suffixe
_utc - Les fuseaux autres que UTC ne sont pas pris en charge sous forme d’horodatage aware
- Si le nom du champ n’a pas le suffixe
- Certains parseurs convertissent des sorties spécifiques à une plateforme et affichent un avertissement s’ils sont exécutés sur une plateforme non prise en charge
- Même sur une plateforme non prise en charge, il reste possible de parser des fichiers de sortie générés sur d’autres systèmes ; dans ce cas, on peut supprimer l’avertissement avec
-qouquiet=Trueen Python - Les plateformes testées incluent Centos 7.7, Ubuntu 18.04/20.04, Fedora32, macOS 10.11.6/10.14.6, NixOS, FreeBSD12, Windows 10, Windows Server 2016 et Windows Server 2019
1 commentaires
Commentaires Hacker News
Sous FreeBSD, ce problème est en partie résolu avec libxo :
on peut obtenir une sortie structurée comme avec
$ ps --libxo=json | jqCe n’est pas parfait : la prise en charge de
lsexistait, mais a été supprimée pour une raison quelconque, et tous les utilitaires ne la prennent pas en chargejcressemble à une excellente solution temporaire, avec de nombreux parseurs de commandes, mais elle a la limite de parser une sortie texte qui n’a pas été conçue à l’origine pour celaCe serait bien que les utilitaires convergent vers une sortie structurée via un flag commun ; même si aller jusqu’au comportement par défaut de PowerShell serait excessif pour Unix/Linux, un simple flag standard
--jsonserait déjà une grande avancéehttps://wiki.freebsd.org/LibXo
https://reviews.freebsd.org/D13959
Si je comprends bien, il ne se contente pas de transmettre des données structurées : il fait circuler des objets opaques dans le pipeline, et l’on peut même revenir à l’étape précédente pour appeler des méthodes : https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_methods?view=powershell-7.4
J’aime bien les wrappers comme
jc,libxo, ainsi que les shells expérimentaux comme https://www.nushell.sh/, mais ils se concentrent davantage sur la transmission de données que sur des objets dotés de méthodes exécutablesLes données structurées restent très « Unix » dans l’esprit, et si l’on a besoin de vrais objets, c’est probablement le moment de lancer Python ou Ruby
Aussi excellent qu’un shell puisse être, et même s’il offre de bonnes fonctionnalités de programmation, il est important de savoir quand passer d’un script shell à un vrai langage de programmation
jcy est présenté comme un outil qui comble ce manque pour l’instant, et semble conçu comme un tremplin en attendant que la prise en charge de-j/--jsonse généralise dans les outils Unixhttps://blog.kellybrazil.com/2019/11/26/bringing-the-unix-philosophy-to-the-21st-century/
/procrenvoient des données JSON plutôt que des fichiers texte non structurésUne solution plus structurelle pourrait consister à permettre l’export de structures de données depuis ELF, à sérialiser ces structures vers une sortie terminal, puis à les afficher ou les traiter dans le format souhaité par l’utilisateur, comme JSON ou YAML
Je ne me souviens plus de l’utilitaire exact, mais c’était probablement
iostat: il formatait les lignes de sortie JSON par interpolation de chaînes, et certaines combinaisons de flags produisaient une sortie complètement casséeJe ne sais pas si cela s’est amélioré depuis, mais avec une option d’intervalle, je m’attendais à quelque chose du type JSON Lines
En matière d’utilisabilité, je trouve que PowerShell et kubectl ont une nette longueur d’avance sur libxo
https://github.com/Juniper/libxo
https://libxo.readthedocs.io/en/latest/
L’idée est vraiment bonne, mais quand on pense à la maintenance, cela rend inquiet
Entre les versions, les variations de sortie selon les flags des commandes, etc., cela ressemble à un enfer à maintenir ; en pratique, cela fonctionnera sans doute bien dans certains cas, mais au-delà des scénarios de base, l’effet de nouveauté risque de s’estomper rapidement
En plus, utiliser
--pour les options de l’outil ne me semble pas idéalSi chaque nouveau parseur nécessite de nouveaux flags, l’aide ou les pages de manuel pourraient atteindre des milliers de lignes
Lorsqu’un utilitaire décide de proposer son propre export JSON, cet outil n’a plus qu’à déléguer à cette fonctionnalité
jc, commejc lsLe paramètre
--cmdpermet de traiter les données avant la conversion, ce qui est plutôt une bonne idéePar exemple, on peut vouloir faire un
grepsur la liste avant de la convertirCôté maintenance, la sortie des commandes Unix de base ne change pas énormément : si elle changeait, cela casserait non seulement cet outil, mais aussi d’innombrables scripts, donc les mises à jour d’autres binaires ne devraient pas le casser aussi souvent qu’on pourrait le penser
Un ensemble commun de parseurs bien maintenu vaut mieux que des parseurs ad hoc dispersés un peu partout, mais lorsqu’on a besoin d’une sortie JSON native pour faire des choses complexes, j’aimerais que le parsing n’ajoute pas lui-même des problèmes
Pour l’utiliser confortablement, il faudra sans doute que j’envoie des PR avec des cas de test supplémentaires pour tout ce que je compte utiliser
De toute façon, ces tests, j’aurais dû les écrire moi-même
Il faut que les gens soumettent les informations de parsing pour les outils dont ils ont besoin, et que les utilisateurs puissent facilement rester à jour
Nushell adopte une approche différente, mais arrive pour l’essentiel au même point : des données structurées pour les commandes du shell
C’est principalement le shell lui-même qui prend ce rôle en charge
http://www.nushell.sh/
from json, ce qui le rend en pratique très complémentaire de JCJ’avais enregistré une vidéo montrant quelques bonnes fonctionnalités de Nushell, et vers 19 minutes je parle de son association avec
jc: https://www.youtube.com/watch?v=KF5dtxVsn1EJ’essayais d’écrire un script pour parcourir des fichiers dans des répertoires correspondant à un certain motif, puis supprimer ceux dont les dates de modification étaient à moins de 10 minutes les unes des autres, et je me suis souvenu que Nushell était bien adapté à ce genre de tâche
Après l’avoir essayé un peu, j’ai enfin eu le déclic, et maintenant je suis accro
Même avec des données non structurées, pouvoir les convertir et les traiter sous une forme comme une liste d’enregistrements donne beaucoup de puissance
Dans un certain sens, le fait qu’il y ait des fichiers partout est excellent, c’est la promesse d’Unix, et Plan 9 pousse cela encore plus loin
Mais le fait qu’il s’agisse de fichiers non structurés, ou que chaque fichier ait son propre format, est tout aussi handicapant
Même parser un seul fichier de logs nginx avec seulement des outils comme
awkpeut devenir pénibleL’un des gros inconvénients est qu’il est difficile de mener de grandes réécritures ou refontes de conception dans l’espace utilisateur Linux
Je veux un shell plus intelligent, j’aime aussi les fichiers, et j’ai toujours un livre sur
awkà portée de main, mais je pense qu’il est temps d’améliorer sérieusement le parsing des donnéesDe la même manière qu’un programme sait décider s’il doit rendre une sortie colorée ou non, ce serait bien qu’il puisse aussi décider s’il doit émettre une sortie structurée
Comme n’importe quelle UI textuelle peut être utilisée comme une API, le texte facile à lire pour les humains passe en priorité
Le scripting shell ressemble donc à l’écriture d’extensions de navigateur tierces
On observe, on devine, puis on bricole un parseur à partir de ces suppositions en espérant que ça marche
Ce serait bien d’avoir une troisième sortie standard dédiée au contenu lisible par les machines
Le terminal ne l’afficherait pas par défaut, mais lors d’un pipe ce serait cette sortie qui serait transmise ; elle devrait être en JSONL, et les pages de manuel préciseraient le contrat
Ainsi, la sortie standard resterait destinée aux humains, et le parsing deviendrait quelque chose qu’on fait en sachant que c’est possible mais fragile
Bien sûr, c’est une idée qui casserait complètement la compatibilité descendante, et si l’on modernisait les CLI de façon irréaliste en les réinventant depuis leurs fondations, la liste des choses à changer serait longue
Obtenir des améliorations est aussi vraiment difficile, et quand quelqu’un comme Lennart essaie de retirer de vieilles scories vieilles de plusieurs décennies, le drame éclate immédiatement
JSON n’est pas encore parfait non plus
JSON est une idée à peu près correcte, mais pour bien faire les choses, il faudrait un format capable d’exposer des choses comme les types de données
Il faudrait quelque chose de plus proche de PowerShell, afin de traiter les nombres comme des nombres et de pouvoir faire des choses étonnantes comme calculer un écart entre dates avec
$a - $bCe serait bien que cela arrive dans GNU coreutils
Le problème est qu’il est tellement enraciné dans .NET et les objets qu’il est très difficile de l’intégrer aux commandes natives existantes, quelle que soit la plateforme
Parce qu’ils font déjà essentiellement la même chose
Mais l’objectif final sera sans doute que les outils de plus haut niveau reconnaissent cette valeur et fournissent eux-mêmes une sortie structurée
Excellent
Je soutiens fortement les outils CLI qui proposent une sortie JSON raisonnable, et des choses comme https://github.com/WireGuard/wireguard-tools/blob/master/contrib/json/wg-json ainsi que le
|ConvertTo-Jsonde PowerShell jouent un grand rôle dans l’automatisation de l’administration et de la supervisionCela dit, ici, raisonnable fait beaucoup de travail, et la réalité reste la réalité
Comme avec la façon dont LSI/Broadcom StorCLI ajoute un
Jderrière les commandes, ou les wrappers de PowerShell qui masquent COM : techniquement, c’est du JSON, mais le résultat est absurdement complexe ou inutile, si bien qu’on revient souvent à l’expédient « faisons juste passer quelques regex sur la sortie en texte brut »Je compte quand même absolument regarder ça
Si le premier exemple, le parsing de la sortie de
dig, représente ce qu’on peut faire de manière fiable, cela pourrait être assez intéressantJe pense que
jc dig example.comdevrait être la syntaxe par défautdig example.com | jc --digdoit en effet deviner après coup les options et paramètres de la commande précédente pour parser sa sortieLe fait que toutes les sorties soient des objets est l’un des aspects que je préfère dans PowerShell
Ça me manque chaque fois que je dois écrire un script bash
Respect à la personne qui s’est chargée de maintenir ça
Un truc comme
aws s3 ls | jc --aws=1.2.3, ce serait un cauchemarÇa me fait penser au programme
filede Linux ou Unix, et aux héros qui le maintiennentplugins, par exemple sous la forme de commandes shell séparées, une partie de l’effort de maintenance pourrait être transférée aux auteurs de pluginsJe me demande s’il existe une liste d’outils en ligne de commande Unix modernes acceptant l’option
--jsonIl pourrait être utile d’ajouter ce type d’information à ce dépôt
ip, le remplaçant moderne deifconfig, prend en charge JSONlldpctlle prend aussi en chargeAnsible fournit les détails du système sous forme de JSON appelés
facts, et l’automatisation est conçue pour les exploiterlsblkaccepte le flag--jsonet peut fournir beaucoup d’informationsOn peut essayer
lsblk --json --output-allC’est très utile lorsqu’un script doit vérifier les disques et partitions du système
-T jsonProjet intéressant
Mais je pensais qu’un outil comme textfsm serait utilisé comme parseur de première étape
textfsm est beaucoup utilisé pour analyser les sorties CLI des équipements réseau
https://github.com/google/textfsm