5 min de lecturaIngeniería

CLI de acortamiento de URL: acorta enlaces desde el terminal

Acorta una URL desde la línea de comandos con curl y jq, conviértelo en una función de shell, copia el resultado al portapapeles y acorta un archivo entero con xargs en paralelo.

Marius Voß
DevRel · edge infra
CLI de acortamiento de URL: un POST de curl canalizado a través de jq para imprimir un enlace corto directamente en el terminal y copiarlo al portapapeles

Acortar una URL desde el terminal es cuestión de una línea:

curl -s -X POST https://api.elido.app/v1/links \
  -H "Authorization: Bearer $ELIDO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"destination_url":"https://example.com/spring-sale"}' | jq -r .short_url

Eso imprime https://s.elido.me/ab12cd y nada más. curl hace la solicitud, jq extrae un campo del JSON y -r elimina las comillas que lo rodean para que el valor pueda ir directamente al portapapeles o a otro comando.

El resto de este artículo presenta las cuatro mejoras que convierten esa línea en algo que realmente usarás: una función de shell, códigos de salida fiables, procesamiento masivo con concurrencia limitada y el portapapeles. Si prefieres escribirlo en un lenguaje en lugar de hacerlo en shell, la misma llamada existe en Python, Go y otros seis entornos de ejecución.

Una canalización de terminal que envía una URL de destino con curl, recibe la respuesta JSON y la canaliza a través de jq para imprimir el enlace corto y copiarlo al portapapeles

Conviértelo en un comando

Un alias no puede aceptar un argumento, así que usa una función. En .zshrc o .bashrc:

short() {
  [ -z "$1" ] && { echo "usage: short <url> [slug]" >&2; return 2; }

  local body
  body=$(jq -n --arg url "$1" --arg slug "${2:-}" \
    '{destination_url: $url} + (if $slug == "" then {} else {slug: $slug} end)')

  curl -s --fail-with-body -X POST https://api.elido.app/v1/links \
    -H "Authorization: Bearer $ELIDO_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: $(printf %s "$1" | shasum -a 256 | cut -d' ' -f1)" \
    -d "$body" | jq -r .short_url
}

Hay tres decisiones deliberadas.

Construir el JSON con jq -n --arg en lugar de interpolar cadenas significa que una URL de destino que contenga comillas o un ampersand no rompe la solicitud. Es el equivalente en shell de SQL parametrizado, y omitirlo pertenece a la misma clase de errores.

--fail-with-body es la opción que suele pasarse por alto. De forma predeterminada, curl devuelve 0 ante cualquier intercambio completado, por lo que un 401 atraviesa la canalización, jq imprime null y el script continúa con null donde debería haber un enlace. Con ella, un 4xx establece un estado de salida distinto de cero y sigues obteniendo el cuerpo del error para leerlo.

La Idempotency-Key derivada de la URL hace que ejecutar el mismo comando dos veces devuelva el mismo enlace en lugar de crear dos. Esto importa más en la sección de procesamiento masivo de abajo, y el razonamiento se explica en límites de velocidad e idempotencia.

¿Necesitas una clave? Crea una en el plan gratuito, expórtala desde el perfil de tu shell y la función estará disponible en el siguiente terminal nuevo.

Directamente al portapapeles

Los últimos caracteres que hacen que parezca terminado:

short "https://example.com/spring-sale" | tee /dev/tty | pbcopy      # macOS
short "https://example.com/spring-sale" | tee /dev/tty | xclip -sel c # Linux, X11
short "https://example.com/spring-sale" | tee /dev/tty | wl-copy     # Wayland
short "https://example.com/spring-sale" | tee /dev/tty | clip.exe    # WSL

tee /dev/tty imprime el enlace y lo copia al mismo tiempo, lo que es mejor que copiar a ciegas y esperar.

Procesamiento masivo sin acumular respuestas 429

Cuatro mejoras desde un curl de una sola línea hasta una herramienta de línea de comandos utilizable: una función de shell, mostrar los errores, procesamiento masivo con xargs en paralelo e integración con el portapapeles

Un archivo de URLs, una por línea, acortadas de ocho en ocho:

export -f short                      # bash; zsh users call the script directly
xargs -P 8 -I{} bash -c 'short "{}"' < urls.txt > short-urls.txt

-P 8 es toda la estrategia frente al límite de velocidad: hay ocho solicitudes en curso como máximo, tanto si el archivo tiene cincuenta líneas como si tiene cincuenta mil. Sin esta opción, xargs las ejecuta tan rápido como puede crear procesos y la API responde con 429 a la mayoría.

Conviene conocer dos salvedades antes de usar esto con un archivo real. xargs -P intercala la salida, así que las líneas pueden llegar desordenadas; si la correspondencia entre la entrada y la salida importa, imprime ambas:

xargs -P 8 -I{} bash -c 'printf "%s\t%s\n" "{}" "$(short "{}")"' < urls.txt > mapping.tsv

Además, una URL que contenga un espacio o una comilla no sobrevivirá a -I{} sin escapar. Para cualquier dato proporcionado por un usuario, xargs -0 con un archivo de entrada delimitado por nulos es la opción segura. Llegados a ese punto, un script real en Python suele requerir menos trabajo que ajustar correctamente las comillas.

Mantén la clave fuera del historial

ELIDO_API_KEY=abc123 short <url> pone la clave en ~/.zsh_history y en la lista de procesos, donde cualquier otra cuenta de la máquina puede leerla con ps.

Expórtala desde el perfil de tu shell o, mejor aún, obtenla de un almacén de secretos al iniciar el shell:

export ELIDO_API_KEY="$(security find-generic-password -s elido -w)"      # macOS Keychain
export ELIDO_API_KEY="$(pass show elido/api-key)"                          # pass

Rotar una clave pasa a significar actualizar una entrada en lugar de buscar en archivos ocultos repartidos entre tres máquinas. El mismo principio se aplica al lado del servidor de cualquier script que programes, y los webhooks para eventos de enlaces son el patrón para recuperar datos sin que haya otra clave guardada en algún sitio.

Cuándo es adecuado el terminal

Es adecuado cuando las URLs ya están en un archivo, cuando ya estás en un shell o cuando el acortamiento es un paso de un script más grande: un proceso de lanzamiento que genera un enlace de distribución, un trabajo de CI que acorta la URL de un despliegue de vista previa o un hook de git que produce un enlace para una entrada del registro de cambios.

No es el lugar adecuado para gestionar una campaña. Las carpetas, las etiquetas y la comparación del volumen de clics entre ubicaciones necesitan un panel, e intentar hacerlo con consultas de jq contra un endpoint de lista es un proyecto, no un atajo. Soluciones para desarrolladores explica qué ofrece la API más allá de la creación, y la API y los SDK cubre los clientes tipados para cuando un script de shell deja de ser suficiente.

Lee la serie principal

Este artículo forma parte del clúster de ingeniería. Empieza por la guía de la API de acortamiento de URL gratuita para conocer la estructura del endpoint y continúa con límites de velocidad e idempotencia. La referencia actualizada está en la documentación de la API.

Relacionado en el blog

Preguntas frecuentes

¿Cómo acorto una URL desde la línea de comandos?

Envía la URL de destino a la API del acortador con curl y canaliza la respuesta a través de jq para extraer el enlace corto. Una sola línea, sin instalar nada aparte de curl y jq, y la clave de API procede de una variable de entorno para que nunca aparezca en el historial del shell.

¿Cómo lo convierto en un comando reutilizable?

Envuelve la llamada a curl en una función de shell dentro de tu .zshrc o .bashrc, tomando la URL como primer argumento. Recarga el perfil y short https://example.com funciona desde cualquier directorio. Aquí una función es mejor que un alias porque necesita aceptar un argumento.

¿Por qué mi canalización de curl termina correctamente cuando la API ha devuelto un 401?

Porque curl devuelve 0 ante cualquier intercambio HTTP completado, incluidas las respuestas de error. Añade --fail-with-body para que un 4xx o 5xx establezca un código de salida distinto de cero y el cuerpo se siga mostrando; de lo contrario, el script continúa con un objeto de error donde debería estar el enlace corto.

¿Cómo acorto un archivo entero de URLs desde el shell?

Canaliza el archivo a xargs con -P 8 para ejecutar ocho solicitudes a la vez y con -I{} para sustituir cada línea. Eso limita la concurrencia a ocho independientemente de la longitud del archivo, que es lo que mantiene un lote grande por debajo del límite de velocidad de la API en lugar de acumular respuestas 429.

¿Cómo copio el enlace corto directamente al portapapeles?

Canaliza la salida a pbcopy en macOS, xclip -selection clipboard en Linux con X11, wl-copy en Wayland o clip.exe en WSL. Combinado con una función de shell, un solo comando produce un enlace que ya está listo para pegar.

¿Dónde debería guardarse la clave de API para un flujo de trabajo con CLI?

En una variable de entorno exportada desde el perfil del shell o, mejor aún, obtenida de un gestor de contraseñas o un almacén de secretos al iniciar el shell. Escribir la clave en línea la deja en el archivo de historial y en la lista de procesos, donde cualquier otro usuario de la máquina puede leerla.

Prueba Elido

Pega una URL, obtén un enlace corto

Sin registro. El enlace vive 30 días. Crea una cuenta para conservarlo.

Gratis, sin registro · 2 por día

Prueba Elido

Acortador de URL alojado en la UE: dominios personalizados, análisis profundo y API abierta. Plan gratuito - sin tarjeta de crédito.

Etiquetas
url shortener cli
shorten url command line
curl url shortener
shorten url terminal
bash shorten link function
xargs parallel api calls

Seguir leyendo