JC convierte la salida de herramientas populares de línea de comandos a JSON
(github.com/kellyjonbrazil)- JC es una herramienta que convierte a JSON la salida de varias herramientas CLI, formatos de archivo y cadenas de texto comunes para facilitar su parsing en scripts
- Recibe entrada por pipe y emite JSON por
STDOUT; también admite magic syntax, que se antepone al comando, como enjc dig example.com - La salida predeterminada usa un esquema estricto por parser y convierte números,
null, booleanos y campos semánticos adicionales; se puede acceder al JSON crudo previo al preprocesamiento con-roraw=True - También puede usarse como biblioteca de Python, por lo que los resultados pueden recibirse como diccionarios de Python, listas de diccionarios o un iterable diferido en vez de JSON
- Para pipelines grandes o de larga ejecución, ofrece parsers de streaming que emiten JSON Lines/NDJSON,
--slurp,--meta-out, plugins de parsers locales y una opción para suprimir advertencias de compatibilidad de plataforma
Qué hace JC
- JC(JSON Convert) convierte a JSON la salida de varias herramientas CLI, formatos de archivo y cadenas de texto comunes
- La salida convertida puede conectarse por pipe con herramientas como
jqojellopara procesamiento adicional - El ejemplo usa
dig example.com | jc --digpara convertir la salida dedigen un arreglo JSON y luegojq -r '.[].answer[].data'para extraer solo las direcciones IP - La misma tarea también puede ejecutarse con magic syntax, como
jc dig example.com | jq -r '.[].answer[].data'
Formas de uso como CLI y biblioteca de Python
- El uso básico de la CLI consiste en recibir entrada por pipe e imprimir una representación JSON
COMMAND | jc [SLICE] [OPTIONS] PARSERcat FILE | jc [SLICE] [OPTIONS] PARSERecho STRING | jc [SLICE] [OPTIONS] PARSER
- La magic syntax antepone
jca un comando o archivo de/proccon el formatojc [SLICE] [OPTIONS] COMMANDojc [SLICE] [OPTIONS] /proc/<path-to-procfile>- No admite alias de comandos ni builtins del shell
- En Python se llama como
jc.parse('dig', cmd_output)- El valor devuelto puede ser un diccionario de Python, una lista de diccionarios o un iterable diferido de un parser de streaming, no una cadena JSON
- La documentación del paquete de Python puede consultarse con
help('jc'),help('jc.lib')o en la documentación en línea
Representación de salida y esquema
- La representación predeterminada usa un esquema estricto por parser
- Los números conocidos se convierten en valores JSON
intofloat - Los valores
Noneconocidos se convierten en JSONnull - Los valores booleanos conocidos también se convierten
- Algunos parsers agregan campos semánticos adicionales
- Los números conocidos se convierten en valores JSON
- Se puede acceder al JSON crudo previo al preprocesamiento con la opción
-rde la CLI oparse(..., raw=True)en la biblioteca de Python - El esquema de cada parser puede consultarse en los enlaces de documentación junto a la lista de parsers
- La salida JSON está en formato compact de forma predeterminada; se puede usar el formato pretty con la opción
-p - También es posible generar salida YAML con las opciones
-yo--yaml-out
Parsers compatibles y ejemplos de uso
- El soporte incluye comandos CLI, formatos de archivo y parsers de cadenas de texto
- La lista de parsers del README incluye
dig,ls,ping,ps,netstat,ifconfig,csv,xml,yaml,/etc/hosts,/etc/passwd,/proc/,systemctl,git log,jwt,url,semver, archivos relacionados conx509, entre otros - Los ejemplos de salida muestran cómo convertir varias entradas en estructuras JSON
- La salida de
arpse convierte en campos de dirección, tipo de hardware, dirección MAC e interfaz - Un archivo CSV se convierte en un arreglo de objetos con los encabezados como claves
/etc/hostsse convierte en IPs y arreglos de hostnamesifconfigse convierte en objetos que incluyen nombre de interfaz, MTU, direcciones IPv4/IPv6, contadores de paquetes y bytes, etc.pingse convierte en objetos que incluyen cantidad de paquetes transmitidos y recibidos, tasa de pérdida, tiempo de ida y vuelta y lista de respuestas
- La salida de
- En Ansible puede usarse como filter plugin de la colección
community.general
Instalación
jcpuede instalarse de varias formaspip3 install jc- Repositorios de paquetes del sistema operativo
- Binarios por arquitectura en GitHub Releases
- Los ejemplos de instalación por paquete del sistema operativo incluyen
- Debian/Ubuntu:
apt-get install jc - Fedora:
dnf install jc - openSUSE:
zypper install jc - Arch:
pacman -S jc - macOS:
brew install jc - FreeBSD: instalación mediante ports
- Ansible filter plugin:
ansible-galaxy collection install community.general - FortiSOAR connector: instalación desde FortiSOAR Connector Marketplace
- Debian/Ubuntu:
Opciones principales
-a/--about: imprime información dejcy de los parsers en JSON o YAML-d/--debug: imprime mensajes de seguimiento cuando hay problemas de parsing;-ddofrece depuración más detallada-h/--help: imprime la ayuda; conjc -h --parser_namese consulta la documentación del parser-M/--meta-out: agrega metadatos a la salida, como timestamp, nombre del parser, magic command y código de salida del magic command-q/--quiet: suprime mensajes de advertencia del parser;-qqignora errores de parsers de streaming-s/--slurp: agrupa entradas de varias líneas en un arreglo-u/--unbuffer: desactiva el buffering de salida-B/--bash-comp,-Z/--zsh-comp: genera un completion script para Bash o Zsh
Slice y Slurp
- Slice procesa solo una parte de las líneas de entrada con una sintaxis
START:STOPsimilar al slicing de Python - Por ejemplo, en una tabla con encabezado y pie, se puede convertir solo la data intermedia a JSON con
jc 1:-1 --asciitableojc 1:4 --asciitable - Los slices positivos y vacíos son los más eficientes en memoria, mientras que los slices negativos usan más memoria
- Slurp permite que los parsers de cadenas que reciben entrada de una sola línea emitan varios elementos a la vez como un arreglo
- Ej.: procesar con
jc --slurp --ip-addressun archivo con varias direcciones IP, una por línea
- Ej.: procesar con
- La magic syntax de
/procadmite slurp automáticamente al seleccionar varios archivos- Al convertir varios archivos de
/proc, se agrega el campo_filepara identificar a qué archivo corresponde cada objeto de resultado
- Al convertir varios archivos de
- Al usar
--meta-outjunto con slurp, se imprime una estructura envuelta con un arregloresulty un objeto_jc_meta
Códigos de salida y metadatos
- Los errores fatales internos de
jcgeneran el código de salida100; en caso contrario, devuelve0 - Al usar magic syntax,
jcguarda el código de salida del programa que se está parseando y lo suma al código de salida dejc- Si
ifconfigdevuelve1yjcdevuelve0, el código de salida combinado es1 - Si
ifconfigdevuelve0yjcdevuelve100, el código de salida combinado es100 - Si ambos fallan, el código de salida combinado es
101
- Si
- Al usar
--meta-outo-Mcon magic syntax, el objeto_jc_metaincluye información del magic command y su código de salida
Colores y variables de entorno
- La variable de entorno
JC_COLORSpermite especificar colores para key name, keyword, number y string- El formato es
JC_COLORS=<keyname_color>,<keyword_color>,<number_color>,<string_color> - Los valores de color deben ser uno de
black,red,green,yellow,blue,magenta,cyan,gray,brightblack,brightred,brightgreen,brightyellow,brightblue,brightmagenta,brightcyan,white,default
- El formato es
- Si se establece la variable de entorno
NO_COLOR, la salida con color se desactiva - La opción
-Ctiene prioridad sobre la variable de entornoNO_COLORy la opción-m, y fuerza la salida con color
Parsers de streaming
- La mayoría de los parsers leen todo
STDINen memoria, lo parsean y luego serializan el documento JSON de salida - Algunos parsers de streaming procesan la entrada línea por línea en cuanto la reciben y emiten JSON Lines o NDJSON
- Ej.:
ls-s,ping-s - Pueden reducir mucho el uso de memoria para salidas grandes de comandos como
ls -lR /y, en algunos casos, procesar más rápido
- Ej.:
- Los parsers de streaming no pueden usarse con magic syntax
- Para evitar que los errores de parsing rompan el pipe en pipelines de larga ejecución, se puede usar
-qqoignore_exceptions=Trueen Python- Las líneas exitosas incluyen
_jc_meta.success: true - Las líneas fallidas incluyen
_jc_meta.success: false,erroryline
- Las líneas exitosas incluyen
- Si la salida aparece con demora entre pipes debido al buffer del sistema operativo, se puede usar salida sin buffer con
-u- Sin embargo, en streams de datos grandes, la salida sin buffer puede ser más lenta
- En Python, los parsers de streaming reciben una entrada iterable y devuelven un objeto iterable para procesamiento diferido
Plugins de parsers
- Un parser plugin local puede colocarse en la carpeta
jc/jcparsersdentro del directorio de datos de la app- Linux/unix:
$HOME/.local/share/jc/jcparsers - macOS:
$HOME/Library/Application Support/jc/jcparsers - Windows:
$LOCALAPPDATA\jc\jc\jcparsers
- Linux/unix:
- Los plugins son archivos de módulo estándar de Python
- Se puede usar
jc/parsers/foo.pyojc/parsers/foo_s.pycomo plantilla - El nombre de archivo del plugin debe ser un nombre válido de módulo de Python, comenzar con una letra y contener solo caracteres alfanuméricos y underscore
- Los plugins locales pueden overridear parsers predeterminados
Locale, zona horaria y compatibilidad
- Para obtener los mejores resultados, se recomienda configurar
LC_ALLenCoen_US.UTF-8 - En algunos sistemas antiguos, el locale
Cno soporta codificación UTF-8, por lo que la salida UTF-8 puede degradarse a ASCII y secuencias de escape\\u - Algunos parsers agregan campos calculados de epoch timestamp
- Si el nombre del campo no tiene el sufijo
_utc, se considera un timestamp naive basado en la zona horaria local - Si se detecta zona horaria UTC en el texto de salida del comando, se convierte en un timestamp aware y el nombre de la clave lleva el sufijo
_utc - Las zonas horarias distintas de UTC no se soportan como timestamps aware
- Si el nombre del campo no tiene el sufijo
- Algunos parsers convierten salidas específicas de cada plataforma, por lo que imprimen advertencias si se ejecutan en plataformas no soportadas
- Incluso en plataformas no soportadas, se pueden parsear archivos de salida de otros sistemas; en ese caso, las advertencias pueden suprimirse con
-qoquiet=Trueen Python - Las plataformas probadas incluyen Centos 7.7, Ubuntu 18.04/20.04, Fedora32, macOS 10.11.6/10.14.6, NixOS, FreeBSD12, Windows 10, Windows 2016 Server y Windows 2019 Server
1 comentarios
Opiniones de Hacker News
En FreeBSD, este problema está resuelto en cierta medida con libxo:
se puede obtener salida estructurada con algo como
$ ps --libxo=json | jqNo es perfecto: había soporte para
ls, pero se eliminó por alguna razón, y no todas las utilidades lo soportan.jcparece una excelente solución temporal que ofrece parsers para muchos comandos, pero tiene la limitación de parsear salidas de texto que, de entrada, no fueron diseñadas para ser parseadas.Sería bueno que las utilidades convergieran en emitir salida estructurada mediante una bandera común; aunque hacer que eso sea el valor predeterminado, como en PowerShell, sería demasiado para Unix/Linux, una bandera estándar
--jsonya sería un gran avance.https://wiki.freebsd.org/LibXo
https://reviews.freebsd.org/D13959
Entiendo que no se limita a pasar datos estructurados, sino que hace fluir objetos opacos por el pipeline y hasta permite volver a una etapa anterior para llamar métodos: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_methods?view=powershell-7.4
Me gustan wrappers como
jc,libxoy shells experimentales como https://www.nushell.sh/, pero se enfocan más en el paso de datos que en objetos con métodos ejecutables.Los datos estructurados todavía se sienten muy Unix; si de verdad se necesitan objetos, creo que es momento de abrir Python o Ruby.
Por muy bueno que sea un shell y por buenas que sean sus funciones de programación, es importante saber cuándo pasar de un script de shell a un lenguaje de programación hecho y derecho.
jcse presenta como una herramienta que cubre ese rol por ahora, y se entiende como un puente hasta que el soporte de-j/--jsonse extienda ampliamente por las herramientas Unix.https://blog.kellybrazil.com/2019/11/26/bringing-the-unix-philosophy-to-the-21st-century/
/procdevuelven datos JSON en lugar de archivos de texto no estructurado.Una solución más estructural podría ser permitir que ELF exponga estructuras de datos, serializar esas estructuras como salida de terminal y luego dejar que el usuario las emita o procese en el formato que quiera, como JSON o YAML.
No recuerdo la utilidad exacta, quizá era
iostat, pero formateaba las líneas de salida JSON mediante interpolación de strings y, con cierta combinación de banderas, generaba una salida completamente rota.No sé si ahora mejoró, pero cuando había una opción de intervalo esperaba algo tipo JSON lines.
En términos de usabilidad, creo que PowerShell y kubectl están muy por delante de libxo.
https://github.com/Juniper/libxo
https://libxo.readthedocs.io/en/latest/
La idea es muy buena, pero pensar en el mantenimiento me inquieta.
Considerando cambios de salida según versiones y banderas de comandos, parece un infierno de mantenimiento; en la práctica funcionará bien en algunos casos, pero más allá de los escenarios básicos probablemente pierda rápido el encanto.
Además, usar
--para las opciones de la herramienta no se ve bien.Si cada parser nuevo necesita una bandera nueva, la ayuda o las páginas de manual podrían terminar con miles de líneas.
Cuando alguna utilidad decida soportar su propia exportación a JSON, después esta herramienta puede delegarle esa función.
jcal comando, como enjc ls.El parámetro
--cmdpermite procesar los datos antes de convertirlos, así que en realidad es una buena idea.Por ejemplo, podrías querer aplicar
grepa una lista antes de la conversión.Desde el punto de vista del mantenimiento, si la salida de los comandos Unix básicos cambiara mucho, rompería no solo esta herramienta sino también muchísimos scripts, así que no parece que vaya a romperse tan seguido por actualizaciones de otros binarios.
Un único paquete común de parsers bien mantenido es mejor que parsers improvisados de salida desperdigados por todos lados, pero cuando necesito salida JSON directa para hacer algo complejo, preferiría que el parseo en sí no agregue más problemas.
Para usar esto con confianza, al final supongo que bastaría con enviar PR con casos de prueba adicionales para todo lo que quiera usar.
De todos modos, esas pruebas las habría tenido que escribir yo.
No queda otra que la gente envíe la información de parseo de las herramientas que necesita y que quienes las usan puedan mantenerla actualizada fácilmente.
Nushell adopta un enfoque distinto, pero llega más o menos al mismo punto: datos estructurados de comandos de shell.
Principalmente, el propio shell se encarga de ese papel.
http://www.nushell.sh/
from json, así que en la práctica encaja muy bien con JC.Hace tiempo grabé un video mostrando algunas buenas funciones de Nushell, y alrededor del minuto 19 hablo de combinarlo con
jc: https://www.youtube.com/watch?v=KF5dtxVsn1EEstaba por escribir un script para revisar archivos dentro de directorios que coincidieran con cierto patrón y eliminar aquellos cuyas horas de modificación estuvieran a menos de 10 minutos entre sí, y recordé que Nushell era bueno para este tipo de cosas.
Lo probé un rato y por fin me hizo clic; ahora estoy enganchado.
Incluso con datos no estructurados, si puedes convertirlos a algo como una lista de registros y procesarlos, es muy potente.
En cierto sentido, es genial que haya archivos en todas partes; esa es la promesa de Unix, y en Plan 9 aparece en una forma aún más extendida.
Pero el hecho de que sean archivos no estructurados, o que cada archivo tenga su propio formato, también termina siendo un freno igual de grande.
Incluso intentar parsear un solo archivo de logs de nginx solo con algo como
awkpuede ser molesto.Una de las grandes desventajas es que en el espacio de usuario de Linux es difícil llevar a cabo reescrituras de sistemas a gran escala o cambios de diseño.
Quiero un shell más inteligente, me gustan los archivos y también tengo el libro de
awka la mano, pero creo que ya es hora de una mejora seria en parseo de datos.Así como un programa puede decidir si renderiza o no salida con colores, estaría bien que también pudiera decidir si emitir o no salida estructurada.
Como cualquier UI de texto puede usarse como API, el texto fácil de leer para humanos obtiene prioridad.
Por eso hacer scripts de shell se parece a crear extensiones de navegador de terceros.
Miras, haces una suposición, improvisas un parser con base en esa suposición y esperas que funcione.
Sería bueno tener una tercera salida estándar para contenido legible por máquinas.
La terminal no la mostraría por defecto; al usar pipes, esa salida sería la que se pipea; debería ser JSONL, y la página del manual especificaría el contrato.
Así, la salida estándar podría quedarse para humanos, y el parseo sería algo posible, pero uno sabría que es frágil.
Claro, es una idea que rompería totalmente la compatibilidad hacia atrás, y si reinventáramos de forma poco realista la CLI desde cero para modernizarla, habría una larga lista de cosas que querría cambiar.
También es realmente difícil conseguir mejoras, y cuando alguien como Lennart intenta limpiar restos obsoletos de hace décadas, enseguida se arma el drama.
JSON tampoco es perfecto todavía.
JSON es una idea razonablemente buena, pero para hacerlo bien se necesita un formato que pueda exponer cosas como tipos de datos.
Tendría que ser algo más cercano a PowerShell, para tratar los números como números y poder hacer cosas sorprendentes como calcular la diferencia entre fechas con
$a - $b.Sería bueno que esto ocurriera en GNU coreutils.
El problema es que está tan profundamente arraigado en .NET y en objetos que resulta muy difícil integrarlo con comandos nativos existentes en cualquier plataforma.
Porque, en esencia, ya están haciendo lo mismo.
Aunque el objetivo final probablemente sea que las herramientas superiores vean el valor y ofrezcan directamente salida estructurada.
Excelente.
Apoyo mucho las herramientas CLI que soportan salida JSON razonable, y cosas como https://github.com/WireGuard/wireguard-tools/blob/master/contrib/json/wg-json y
|ConvertTo-Jsonde PowerShell son una parte importante de la automatización de administración/monitoreo.Pero aquí razonable hace mucho trabajo, y la realidad es la realidad.
Como la forma de LSI/Broadcom StorCLI de agregar
Jdespués del comando, o los wrappers de PowerShell que ocultan COM: técnicamente son JSON, pero el resultado es tan absurdamente complejo o inútil que muchas veces uno termina volviendo al parche de “mejor pasarle unas regex a la salida en texto plano”.Aun así, definitivamente voy a revisar esto.
Si el primer ejemplo, parsear la salida de
dig, representa algo que se puede hacer de forma confiable, podría ser bastante interesante.Creo que
jc dig example.comdebería ser la sintaxis predeterminada.Porque con
dig example.com | jc --dig, para parsear la salida luego hay que adivinar las flags y parámetros del comando anterior.Que toda salida sea un objeto es una de las cosas que más me gustan de PowerShell.
Lo extraño cada vez que tengo que escribir un script en bash.
Mis respetos para quien decidió mantener esto.
Algo como
aws s3 ls | jc --aws=1.2.3sería una pesadilla.Me recuerda al programa
filede Linux o Unix, y a los héroes que lo mantienen.plugins, por ejemplo teniéndolos como comandos de shell separados, se podría trasladar parte del esfuerzo de mantenimiento a los autores de plugins.Me pregunto si alguien conoce una lista de herramientas modernas de línea de comandos de Unix que acepten la opción
--json.También podría ser útil agregar esa información a este repositorio.
ip, el reemplazo moderno deifconfig, soporta JSON.lldpctltambién lo soporta.Ansible ofrece los detalles del sistema como JSON llamado
facts, y está diseñado para que la automatización los aproveche.lsblkacepta la bandera--jsony puede dar mucha información.Puedes probar
lsblk --json --output-all.Es muy útil cuando un script necesita revisar los discos y particiones del sistema.
-T json.Es un proyecto interesante.
Pero pensé que usaría algo como textfsm como parser de primera etapa.
textfsm se usa mucho para parsear la salida CLI de equipos de red.
https://github.com/google/textfsm