fx: ver, filtrar y transformar JSON desde la terminal
Abres la respuesta de una API en la terminal y te encuentras 16.000 líneas de JSON sin formatear.
Tu primer instinto es | jq .. El segundo, cuando ves que el resultado sigue sin caber en la pantalla, es abrir el navegador y pegarlo en un visor online. El tercero, si el JSON tiene datos que no deberían salir de tu máquina, es arrepentirte del segundo.
fx existe para el hueco que hay entre esas tres reacciones: un visor interactivo de JSON que se abre en la propia terminal, te deja plegar y desplegar nodos con las flechas, buscar dentro, copiar la ruta de cualquier campo, y salir. Y cuando no quieres interfaz, el mismo binario funciona como procesador en un pipe, con expresiones en JavaScript en vez de un lenguaje propio.
Esto es lo que vas a encontrar aquí:
- Qué es fx, quién lo mantiene y en qué se diferencia de jq de verdad
- Cómo instalarlo en macOS, Linux y Windows, con el aviso importante sobre el paquete de npm
- El modo interactivo: navegación, plegado, búsqueda y las teclas que sí vas a usar
- El modo pipe: expresiones JavaScript, el azúcar sintáctico de
@y?, y las funciones que trae de serie - Cómo ampliarlo con
.fxrc.jspara meter tus propias funciones - Los números reales de rendimiento frente a jq, y las tres cosas que conviene vigilar
¿Qué es fx y quién está detrás? ¶
fx es un visor y procesador de JSON para terminal escrito en Go, distribuido como un único binario sin dependencias. Lo mantiene Anton Medvedev, el mismo autor de walk (gestor de ficheros en terminal) y de la librería expr. El proyecto ronda las 20.000 estrellas en GitHub y está publicado con licencia MIT.
El nombre viene de f(x), function execution. Y esa es exactamente la tesis: en vez de inventarse un lenguaje de consultas, fx trata cada argumento como una función de JavaScript y encadena la salida de una con la entrada de la siguiente.
echo '{"name": "world"}' | fx 'x => x.name' 'x => `Hello, ${x}!`'
Si ya sabes JavaScript —y si estás leyendo esto, probablemente sí— no tienes que aprender nada nuevo. Esa es la diferencia de fondo con jq, cuyo lenguaje es potente pero exige memorizar una sintaxis que solo sirve para jq.
Pero fx tiene una segunda cara que jq no tiene: si lo ejecutas sin ninguna expresión, abre una interfaz interactiva.
fx package-lock.json
Ahí dentro te mueves con j y k, pliegas con h, despliegas con l, buscas con / y copias la ruta del nodo bajo el cursor. Es un explorador de árbol, no un formateador.
🔑 La forma corta de entenderlo: jq es un lenguaje de consultas con un formateador incorporado. fx es un explorador de árbol con un intérprete de JavaScript incorporado. Se solapan, pero no resuelven el mismo problema.
¿En qué se diferencia fx de jq en la práctica? ¶
La comparación honesta necesita separar tres ejes: el lenguaje, la interfaz y el rendimiento.
| fx | jq | |
|---|---|---|
| Lenguaje de expresiones | JavaScript (motor goja) | DSL propio de jq |
| Curva de entrada | Nula si sabes JS | Media-alta |
| Modo interactivo | Sí, TUI completa | No |
| Formatos de entrada | JSON, YAML, TOML, texto plano | JSON (y JSON Lines) |
| Rendimiento en ficheros grandes | Aceptable | Notablemente mejor |
| Extensibilidad | .fxrc.js con funciones propias |
Módulos .jq |
| Tamaño del binario | ~18 MB | ~1 MB |
| Presencia en sistemas | Hay que instalarlo | Suele venir preinstalado o casi |
La tabla deja claro que no es un reemplazo. Es una herramienta distinta que se solapa en el 60% de los casos de uso.
Donde fx gana sin discusión es en exploración: cuando no sabes qué hay dentro del JSON. Recibes un webhook desconocido, un composer.lock de un proyecto heredado, la respuesta de una API que documentaron en 2019. Ahí jq te obliga a jugar a las adivinanzas escribiendo consultas a ciegas; fx te deja abrir el árbol y mirar.
Es el mismo problema al que te enfrentas cuando extraes datos de una web y tienes que entender la estructura de objetos y diccionarios que te devuelve: antes de consultar hay que saber qué forma tiene el dato.
Donde jq gana es en producción: scripts que se ejecutan mil veces al día, ficheros de cientos de megas, pipelines de CI. Ahí los números importan y luego los veremos.
Tu caja de herramientas empieza con dos y va creciendo
Instalar fx es sumar una pieza más a un entorno de terminal que se construye poco a poco. Ese es justo el recorrido de este curso-juego de 17 paradas: montar la carpeta, el login y la terminal, elegir agente con presupuesto en la mano y salir con una checklist a medida en vez de con una lista de deseos.
Entra en el curso gratis →¿Cómo se instala fx? ¶
fx se distribuye por casi todos los gestores de paquetes habituales. Elige el que ya uses:
# macOS y Linux con Homebrew
brew install fx
# Arch Linux
pacman -S fx
# Windows con Scoop
scoop install fx
# Nix
nix-shell -p fx
# Snap
snap install fx
# Docker
docker run -i antonmedv/fx
Si tienes Go instalado, la vía más directa y la que garantiza la última versión:
go install github.com/antonmedv/fx@latest
Y existe un script de instalación que detecta tu sistema y deja el binario en /usr/local/bin:
curl https://fx.wtf/install.sh | sh
Comprueba que ha funcionado:
fx --version
En el momento de escribir esto la versión estable es la 39.2.0.
El aviso sobre el paquete de npm ¶
Aquí hay una trampa que merece un párrafo propio, porque no está señalizada en ningún sitio y te puede costar media hora.
En npm existe un paquete llamado fx, y se instala así:
npm i -g fx
No es el mismo programa. Es una implementación distinta, escrita en JavaScript, no interactiva, que el autor mantiene en paralelo dentro del mismo repositorio. Comparte filosofía y buena parte de la sintaxis, pero no todo el azúcar sintáctico coincide.
Lo comprobé ejecutando los dos. El ejemplo que aparece en el README del paquete de npm:
echo '[{"name": "world"}]' | fx 'map(`Hello, ${x.name}!`)'
Funciona en la versión de npm. En el binario de Go devuelve un TypeError, porque ahí dentro de map() la variable x sigue apuntando al array completo, no a cada elemento. El equivalente correcto en el binario de Go usa el prefijo @, que veremos más abajo.
⚠️ Si buscas documentación de fx y encuentras ejemplos que no te funcionan, comprueba primero cuál de las dos implementaciones tienes instalada.
fx --versiondevolviendo un número como39.2.0indica el binario de Go; el paquete de npm lleva su propio versionado.
Para el resto del artículo, todo lo que leas se refiere al binario de Go, que es el proyecto principal y el que trae la interfaz interactiva.
¿Cómo se navega un JSON grande en el modo interactivo? ¶
Lanza fx con un fichero y sin más argumentos, o mándale datos por una tubería:
fx data.json
curl -s https://api.github.com/repos/antonmedv/fx | fx
Se abre la interfaz a pantalla completa con el JSON plegado por niveles. A partir de ahí, la navegación es deliberadamente vim-like: si vienes de vim o de less, ya sabes casi todo. Es la misma corriente que ha traído interfaces de terminal a pantalla completa para tareas que antes vivían en el navegador, y que hace que cada vez salgas menos de la consola.
Moverte por el árbol:
| Tecla | Acción |
|---|---|
j / k o flechas |
Bajar / subir una línea |
J / K |
Saltar al siguiente / anterior hermano del nodo |
space o f / b |
Página abajo / arriba |
Ctrl+d / Ctrl+u |
Media página abajo / arriba |
g / G |
Ir al principio / al final |
[ / ] |
Atrás / adelante en el historial de saltos |
Ese J mayúscula para saltar entre hermanos es el atajo que más cambia la experiencia. En un array de 500 objetos te deja recorrer elemento a elemento sin atravesar el contenido de cada uno.
Plegar y desplegar:
| Tecla | Acción |
|---|---|
l / enter / flecha derecha |
Desplegar el nodo |
h / backspace / flecha izquierda |
Plegar el nodo |
L / H |
Desplegar / plegar en cascada |
e / E |
Desplegar todo / plegar todo |
1–9 |
Plegar hasta el nivel N |
Las teclas del 1 al 9 son la joya escondida. Pulsar 2 sobre un JSON de diez niveles te deja a la vista solo la estructura de los dos primeros: es la forma más rápida de entender la forma de un documento que no conoces.
Buscar y saltar:
| Tecla | Acción |
|---|---|
/ |
Buscar por expresión regular |
n / N |
Siguiente / anterior resultado |
@ |
Ir a un símbolo (búsqueda difusa de claves) |
Ctrl+g |
Seguir una referencia $ref de JSON Schema |
: + número |
Ir a una línea concreta |
La búsqueda con / acepta expresiones regulares, no solo texto literal. Y Ctrl+g sobre un $ref de un esquema OpenAPI te lleva a la definición apuntada, que si alguna vez has navegado un openapi.json a mano sabes lo que ahorra.
Actuar sobre el nodo:
| Tecla | Acción |
|---|---|
y |
Copiar (abre submenú) |
d |
Borrar el nodo de la vista |
p |
Previsualizar el nodo |
P |
Imprimir por stdout |
v |
Abrir el fichero en tu editor |
z |
Alternar el ajuste de líneas largas |
s |
Mostrar tamaños o números de línea |
? |
Ayuda con todos los atajos |
q |
Salir |
El submenú de y es lo que más vas a usar y conviene aprendérselo: tras pulsar y, la segunda tecla decide qué se copia. y otra vez o v copia el valor, k copia la clave, p copia la ruta del nodo, y b copia el par clave-valor completo.
Ese yp —copiar la ruta— es la razón por la que mucha gente instala fx. Navegas hasta el campo que te interesa dentro de una respuesta anidada, pulsas yp, y ya tienes .data.attributes.items[3].sku en el portapapeles para pegarlo en tu código.
💡 Si solo memorizas dos combinaciones de todo el artículo, que sean
yppara copiar la ruta del nodo y los números1–9para plegar por niveles. Con esas dos ya justificas la instalación.
La tecla v abre el fichero en tu editor y, si usas vim, nvim o Helix, lo abre posicionado en la línea del nodo donde estabas. Se controla con FX_EDITOR o, en su defecto, con EDITOR; si no defines ninguna, tira de vim.
Y sí, hay un huevo de pascua: fx --game-of-life ejecuta el juego de la vida de Conway en la terminal. No aporta nada, pero está ahí.
Los atajos que de verdad usas en una herramienta casi nunca están en su portada. Cada domingo mando 12 recursos seleccionados sobre herramientas y productividad al día a día de programar. Ya somos +6.700.
Quiero esa dinamita 🧨¿Cómo se usa fx sin abrir la interfaz? ¶
En cuanto le pasas una expresión como argumento, fx deja de ser interactivo y se comporta como un filtro de toda la vida: lee, transforma, escribe por stdout y termina.
La regla base es que cada argumento es una función y la salida de una alimenta a la siguiente:
echo '{"name": "world"}' | fx 'x => x.name' 'x => `Hello, ${x}!`'
# Hello, world!
Escribir x => en cada paso cansa, así que fx incorpora atajos. Los revisé contra el código de transpilación del proyecto (internal/engine/transpile.go) y los comprobé uno a uno:
| Escribes | fx lo convierte en | Qué hace |
|---|---|---|
. |
x |
La entrada tal cual |
.foo |
x.foo |
Accede a la propiedad |
.[0] |
x[0] |
Accede al índice |
foo |
foo |
Llama a la función foo |
@.foo |
map(...) |
Aplica la expresión a cada elemento |
?.foo > 42 |
filter(...) |
Filtra el array por la condición |
.a[].b[] |
flatMap encadenado |
Aplana niveles anidados |
Con eso, el ejemplo de antes se queda en:
echo '{"name": "world"}' | fx '.name' '`Hello, ${this}!`'
# Hello, world!
Fíjate en this: dentro de cada expresión, tanto x como this apuntan al dato de entrada de ese paso.
El azúcar de @ y ? ¶
Estos dos prefijos son lo que hace que fx sea agradable de escribir en el día a día. @ es map, ? es filter:
# Extraer un campo de cada elemento
echo '[{"name":"world"},{"name":"moon"}]' | fx '@.name'
# [ "world", "moon" ]
# Filtrar por condición
echo '[{"n":1},{"n":50}]' | fx '?.n > 42'
# [ { "n": 50 } ]
Y se encadenan, que es donde brillan:
echo '[{"n":"a","v":10},{"n":"b","v":50},{"n":"c","v":90}]' \
| fx '?.v > 20' '@.n'
# [ "b", "c" ]
Se lee casi como una frase: filtra los que tengan v mayor que 20, quédate con el campo n. Sin paréntesis, sin select(), sin pipes internos.
El tercer atajo es [] para aplanar, que sustituye a un flatMap anidado:
echo '{"issues":[{"labels":["a","b"]},{"labels":["c"]}]}' | fx '.issues[].labels[]'
# [ "a", "b", "c" ]
Esa expresión equivale a escribir x.issues.flatMap(x => x.labels.flatMap(x => x)). El ahorro es evidente.
Construir objetos nuevos ¶
Como la expresión es JavaScript de verdad, puedes devolver lo que quieras. Solo recuerda envolver los objetos literales entre paréntesis para que no se interpreten como bloques:
echo '{"a":1,"b":2}' | fx '({total: x.a + x.b, keys: Object.keys(x)})'
{
"total": 3,
"keys": [ "a", "b" ]
}
Y cualquier función global de JavaScript está disponible sin más:
echo '{"a":1,"b":2}' | fx 'Object.keys'
¿Qué funciones trae fx de serie? ¶
fx precarga una librería estándar propia antes de evaluar tus expresiones. Estas son las que hay, comprobadas ejecutándolas contra la versión 39.2.0:
Para arrays: uniq, sort, sortBy(fn), reverse, flatten, chunk(n), zip(...), groupBy(fn | "clave"), list.
Para objetos: keys, values, sortKeys, del(clave), walk(fn).
Generales: len, map(fn), filter(fn), apply(fn, ...args).
De control y utilidad: skip, exit(código), save, toBase64, fromBase64, YAML.stringify, YAML.parse, console.log.
Unos cuantos ejemplos de las más útiles:
# Agrupar por una clave (acepta función o nombre de campo)
echo '[{"t":"js","v":1},{"t":"go","v":2},{"t":"js","v":3}]' | fx 'groupBy("t")'
# Trocear en lotes de 2
echo '[1,2,3,4,5]' | fx 'chunk(2)'
# [ [ 1, 2 ], [ 3, 4 ], [ 5 ] ]
# Ordenar las claves de un objeto en profundidad
echo '{"b":1,"a":2}' | fx 'sortKeys'
# Recorrer el árbol entero transformando valores
echo '{"a":{"b":1}}' | fx 'walk(x => typeof x === "number" ? x*10 : x)'
# { "a": { "b": 10 } }
# Contar elementos (funciona en arrays, strings y objetos)
echo '{"a":1,"b":2}' | fx 'len'
# 2
len merece una nota: cuenta elementos en arrays, caracteres en cadenas y claves en objetos. Es el equivalente al length de jq pero sin las sorpresas.
Dos funciones que no son transformaciones sino efectos:
# list imprime cada elemento en una línea y no devuelve nada
echo '["a","b"]' | fx 'list'
# a
# b
# exit corta con el código que le des (útil en scripts y CI)
echo '{"ok":false}' | fx '.ok ? this : exit(1)'
echo $? # 1
Ese exit(1) convierte a fx en una herramienta de verificación dentro de un pipeline: si el JSON no cumple la condición, el script falla.
¿Cómo se procesan varios JSON seguidos con --slurp? ¶
fx lee streams de JSON, no un único documento. Si le mandas objetos pegados o separados por saltos de línea, aplica las expresiones a cada uno por separado:
printf '{"name":"hello"}\n{"name":"world"}\n' | fx '.name'
# hello
# world
Cuando lo que quieres es tratarlos como un solo array, ahí entra --slurp (o -s):
printf '{"name":"hello"}\n{"name":"world"}\n' \
| fx --slurp '.map(x => x.name)' '.join(", ")'
# hello, world
Y para datos que no son JSON en absoluto, --raw (o -r) trata cada línea como una cadena:
ls | fx -r '[this, this.includes(".md")]'
Los dos se combinan con -rs, que es la forma de convertir cualquier salida de terminal en un array de cadenas manipulable con JavaScript:
ls | fx -rs '.filter(x => x.includes(".md"))'
Aquí aparece el símbolo skip, que sirve para no imprimir nada en una iteración concreta:
ls | fx -r '.includes(".md") ? this : skip'
Ese one-liner es un grep escrito en JavaScript. Absurdo para filtrar por .md, muy práctico cuando la condición es una lógica que a grep le costaría expresar.
Esta capacidad de encadenarse con cualquier cosa que escriba por stdout es lo que hace que fx encaje bien con el resto de tu caja de herramientas de terminal. Va igual de fino recibiendo la salida de curl que la de una herramienta como Summarize.sh, que también acepta entrada por pipes.
¿Funciona fx con YAML y TOML? ¶
Sí, y es una de las razones por las que acaba quedándose instalado. Los flags --yaml y --toml convierten la entrada a JSON antes de procesarla, así que todas las expresiones funcionan igual:
# Sacar un campo de un docker-compose.yml
fx --yaml docker-compose.yml '.services'
# Leer un Cargo.toml o un pyproject.toml
fx --toml pyproject.toml '.project.dependencies'
El camino de vuelta también existe. YAML.stringify convierte JSON a YAML, lo que da un conversor bidireccional en una línea:
echo '{"a":1,"b":[1,2]}' | fx 'YAML.stringify'
a: 1
b:
- 1
- 2
Un detalle que agradecerás: el parser de fx es permisivo por defecto. Acepta comas finales, comentarios estilo // y valores como NaN, cosas que romperían a un parser estricto. Es justo lo que quieres cuando estás inspeccionando un tsconfig.json con comentarios o un fichero a medio escribir.
Si prefieres el comportamiento contrario, --strict lo activa:
echo '{"a":1,}' | fx --strict .
# Trailing comma is not allowed in strict mode on line 1.
Los mensajes de error, por cierto, son de lo mejor de la herramienta. Señalan la posición exacta con un cursor debajo, tanto en el JSON de entrada como en tus expresiones:
.a.b.c
^^^^^^
x.a.b.c
TypeError: Cannot read property 'c' of undefined
Te muestra la expresión que escribiste, el JavaScript en el que se transpiló y el error. Depurar una expresión larga deja de ser adivinar.
Del fichero de configuración a la skill
La herramienta se vuelve tuya cuando escribes tú las piezas
Un .fxrc.js convierte lo que repites en una palabra. Con tus agentes de IA pasa lo mismo, y la pieza equivalente se llama SKILL.md: anatomía del fichero, progressive disclosure y plantillas reales que funcionan en Claude Code, OpenCode, Copilot y 25 agentes más.
Abrir la guía →Plantillas SKILL.md descargables incluidas
¿Cómo se amplía fx con .fxrc.js? ¶
Aquí es donde fx pasa de herramienta a entorno propio. Puedes definir tus funciones en un fichero .fxrc.js y quedan disponibles como si fueran parte de la librería estándar.
fx busca ese fichero en varios sitios y, lo importante, los concatena todos en vez de quedarse con el primero. Revisé el orden en internal/engine/fxrc.go:
.fxrc.jsen el directorio actual.fxrc.jsen tu directorio home$XDG_CONFIG_HOME/fx/.fxrc.js(por defecto~/.config/fx/.fxrc.js)fx/.fxrc.jsdentro de cada ruta de$XDG_CONFIG_DIRS(por defecto/etc/xdg)
Que se acumulen en vez de sobrescribirse tiene una consecuencia práctica muy buena: puedes tener tus utilidades generales en ~/.fxrc.js y añadir un .fxrc.js específico en la raíz de un proyecto con funciones que solo tienen sentido ahí. Ambos estarán disponibles a la vez.
El contenido es JavaScript normal. Declara funciones y ya:
// ~/.fxrc.js
function addOne(x) {
return x + 1
}
// Convierte céntimos en un importe legible
function money(x) {
return `${(x / 100).toFixed(2)} €`
}
// Constantes que usas a menudo
const TAX = 1.21
Y desde ese momento son parte del vocabulario de fx:
echo '1' | fx addOne
# 2
echo '1999' | fx money
# 19.99 €
echo '100' | fx 'this * TAX'
# 121
Lo mejor es que se combinan con el azúcar sintáctico como si fueran nativas:
echo '[100,200]' | fx '@money'
# [ "1.00 €", "2.00 €" ]
🔑 Esta es la diferencia real entre usar fx un par de veces y adoptarlo. Las tres o cuatro transformaciones que repites cada semana —normalizar fechas, formatear importes, extraer el campo que tu API anida en tres niveles— se convierten en una palabra.
Un ejemplo con más recorrido, pensado para el package.json de un monorepo:
// .fxrc.js en la raíz del proyecto
// Lista las dependencias que no están fijadas a una versión exacta
function loose(pkg) {
return Object.entries({...pkg.dependencies, ...pkg.devDependencies})
.filter(([, version]) => /^[\^~]/.test(version))
.map(([name, version]) => `${name}@${version}`)
}
fx package.json loose
Una precisión sobre var, let y const ¶
La documentación del paquete de npm recomienda usar var en lugar de let o const para las variables globales del .fxrc.js. Lo comprobé en el binario de Go y ahí const funciona sin problemas, porque fx concatena tu fichero con su librería estándar en un único script antes de evaluarlo, así que todo queda en el mismo ámbito.
Esa recomendación aplica a la implementación de npm, que importa el fichero como módulo. Si escribes tu .fxrc.js pensando en las dos, usa var y te ahorras el problema.
¿Qué variables de entorno configuran fx? ¶
fx no tiene fichero de configuración para su aspecto: se ajusta con variables de entorno. Estas son las que reconoce, sacadas del código fuente:
| Variable | Qué hace |
|---|---|
FX_THEME |
Tema de color. Por defecto 1 |
FX_INDENT |
Espacios de indentación (número o cadena literal) |
FX_COLLAPSED |
Arranca con todo plegado |
FX_LINE_NUMBERS |
Muestra números de línea |
FX_SHOW_SIZE |
Muestra el tamaño de cada colección |
FX_NO_MOUSE |
Desactiva el soporte de ratón |
FX_EDITOR |
Editor para la tecla v (si no, usa EDITOR, y si no, vim) |
Los temas disponibles son 0 (sin color) hasta 9, más cuatro con nombre emoji: 🔵, 🥝, 🔥 y 🟣. Para verlos todos:
fx --themes
Si le pasas un tema que no existe, fx te lista los válidos en el error, que es el detalle de diseño que uno agradece a las cuatro de la tarde.
La indentación acepta tanto un número como una cadena:
echo '{"a":{"b":1}}' | FX_INDENT=4 fx .
Y para dejarlo permanente, la línea de siempre en tu .zshrc o .bashrc:
export FX_THEME="🔥"
export FX_INDENT=2
fx también genera el script de autocompletado para tu shell, que completa rutas del JSON mientras escribes la expresión:
fx --comp bash # o zsh, o fish
Cuatro variables de entorno y un fichero de configuración: así se vuelve tuya una herramienta. En la newsletter compartimos cada semana lo que vamos afinando en nuestro entorno de trabajo, y los suscriptores aportan lo suyo. Gratis desde 2018.
Suscríbete gratis →¿Es fx más rápido que jq? ¶
No. Y conviene decirlo con números en vez de con matices.
Generé un JSON de 16 MB con 200.000 objetos y ejecuté la misma operación con las dos herramientas en la misma máquina:
| Operación | fx 39.2.0 | jq 1.7 |
|---|---|---|
| Contar elementos | 3,1 s | 0,7 s |
| Filtrar por un campo y contar | 2,7 s | 0,9 s |
jq es entre tres y cuatro veces más rápido. Tiene sentido: jq es C compilado con un parser escrito para esto, mientras que fx arranca un intérprete de JavaScript (goja) y construye una estructura de árbol pensada para poder navegarla.
¿Importa? Depende del uso:
- En exploración interactiva, no. Tres segundos abriendo un fichero de 16 MB que vas a inspeccionar durante cinco minutos es ruido.
- En un script que se ejecuta una vez al día, tampoco.
- En un pipeline de CI que procesa cientos de ficheros, o en un bucle que llama a la herramienta miles de veces, sí. Ahí usa jq.
⚠️ La regla que uso: fx para mirar y para escribir transformaciones puntuales que quiero legibles; jq para lo que se automatiza y se repite. No compiten por el mismo hueco del
.bashrc.
Sobre el peso, el binario de fx ocupa unos 18 MB frente al megabyte escaso de jq. Irrelevante en tu portátil, algo a tener en cuenta en una imagen de Docker que quieras mínima.
¿Qué hay que vigilar antes de adoptarlo? ¶
Tres cosas que descubrí probando y que no están señalizadas en la documentación.
La edición in-place y su código de salida. fx tiene una función save que sobrescribe el fichero de entrada con el resultado:
fx data.json 'x.name = x.name.toUpperCase(), x' 'save'
El fichero se guarda correctamente, eso lo verifiqué. Pero el comando termina con código de salida 1 y un error de parseo por pantalla. La causa está en el código: save reescribe el fichero mientras el parser lo sigue leyendo, así que el lector encuentra basura al final. Si vas a usar save dentro de un script con set -e, tenlo presente: el fichero estará bien, el código de salida mentirá.
Las dos implementaciones. Ya lo vimos, pero insisto porque es la fuente número uno de confusión con esta herramienta. El paquete de npm y el binario de Go no aceptan exactamente la misma sintaxis. Los ejemplos de map() con plantillas del README de npm fallan en el binario de Go, donde el equivalente es @.
El tamaño del ecosistema. jq lleva más de una década y tiene respuestas en Stack Overflow para cualquier cosa que se te ocurra. Con fx, cuando te atasques, la fuente será el código fuente. La ventaja compensadora es que el código está en Go y es legible: encontrar cómo funciona el orden de búsqueda de .fxrc.js me llevó dos minutos de leer un fichero de setenta líneas.
Nada de esto lo descalifica. Son los bordes ásperos normales de una herramienta mantenida por una persona, y ninguno afecta al caso de uso principal, que es abrir un JSON y mirarlo.
Preguntas frecuentes sobre fx ¶
¿Qué es fx exactamente?
fx es un visor y procesador de JSON para terminal escrito en Go. Se distribuye como un binario único y funciona en dos modos: una interfaz interactiva para explorar documentos plegando y desplegando nodos, y un modo de tubería donde transforma datos con expresiones de JavaScript.
¿Cómo se instala fx?
Con brew install fx en macOS y Linux, scoop install fx en Windows, pacman -S fx en Arch, o go install github.com/antonmedv/fx@latest si tienes Go. También hay un script: curl https://fx.wtf/install.sh | sh.
¿Es fx un sustituto de jq?
No del todo. fx aporta un modo interactivo que jq no tiene y usa JavaScript en vez de un lenguaje propio, pero jq es entre tres y cuatro veces más rápido en ficheros grandes y está preinstalado en más sistemas. Lo habitual es tener los dos.
¿Necesito saber jq para usar fx?
No. Esa es su principal ventaja: si sabes JavaScript, ya sabes escribir expresiones para fx. Los únicos atajos propios son .campo, @ para map, ? para filter y [] para aplanar.
¿Cómo copio la ruta de un campo dentro de un JSON grande?
En el modo interactivo, navega hasta el campo y pulsa y seguido de p. Copia al portapapeles la ruta completa del nodo, lista para pegar en tu código.
¿Puede fx leer YAML y TOML?
Sí, con los flags --yaml y --toml. Los convierte a JSON antes de procesarlos, así que todas las expresiones funcionan igual. Con YAML.stringify haces también la conversión inversa.
¿Qué es el fichero .fxrc.js?
Es el fichero donde defines tus propias funciones para que fx las reconozca como si fueran nativas. fx lo busca en el directorio actual, en tu home y en las rutas XDG, y concatena todos los que encuentre en vez de quedarse con uno.
¿Cómo cambio el tema de colores de fx?
Con la variable de entorno FX_THEME. Los valores válidos van del 0 (sin color) al 9, más 🔵, 🥝, 🔥 y 🟣. Ejecuta fx --themes para verlos todos antes de decidir.
¿El paquete fx de npm es el mismo programa?
No. Es una implementación en JavaScript, sin modo interactivo, que el autor mantiene en paralelo. Comparte filosofía pero no toda la sintaxis. El proyecto principal es el binario de Go.
¿Sirve fx para scripts y CI?
Funciona, y la función exit(código) permite que un pipeline falle si el JSON no cumple una condición. Pero para trabajo repetitivo con muchos ficheros, jq rinde bastante mejor.
TL;DR ¶
- 🔍 fx es un visor de JSON interactivo para terminal más un procesador tipo jq, en un único binario de Go sin dependencias
- ⚡ Se instala con
brew install fx,scoop install fxogo install github.com/antonmedv/fx@latest - ⌨️ En el modo interactivo,
ypcopia la ruta de cualquier nodo y las teclas1–9pliegan el árbol por niveles: con esas dos ya lo amortizas - 🧩 En el modo pipe usa JavaScript, con
@para map,?para filter y[]para aplanar, sin aprender un lenguaje nuevo - 🛠️ Un fichero
.fxrc.jsconvierte tus transformaciones habituales en funciones propias, y fx concatena todos los que encuentre - 📊 jq sigue siendo tres o cuatro veces más rápido en ficheros grandes: fx para explorar, jq para automatizar
Fuentes ¶
- Repositorio oficial de fx en GitHub — código fuente, del que salen los atajos de teclado, las reglas de transpilación y el orden de búsqueda de
.fxrc.js - Documentación de fx — sitio oficial con la guía de instalación
- README del paquete fx en npm — documentación de la implementación en JavaScript
- Las mediciones de rendimiento y todos los ejemplos de código están comprobados contra fx 39.2.0 y jq 1.7
🧨 Última oprtunidad para recibir la dinamita que mereces sobre programación con IA el próximo domingo: Suscríbete gratis a Web Reactiva en https://webreactiva.com/newsletter
12 recursos para developers cada domingo en tu bandeja de entrada
Además de una skill práctica bien explicada, trucos para mejorar tu futuro profesional y una pizquita de humor útil para el resto de la semana. Gratis.