4 мин чтенияИнженерия

Сокращатель URL в CLI: сокращайте ссылки из терминала

Сокращайте URL из командной строки с помощью curl и jq, оборачивайте команду в shell-функцию, копируйте результат в буфер обмена и сокращайте целый файл с помощью параллельного xargs.

Marius Voß
DevRel · edge infra
CLI сокращателя URL: POST-запрос curl, переданный через jq, выводит короткую ссылку прямо в терминал и в буфер обмена

Сократить URL из терминала можно одной строкой:

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

Она выводит https://s.elido.me/ab12cd и больше ничего. curl выполняет запрос, jq извлекает одно поле из JSON, а -r убирает окружающие кавычки, чтобы значение можно было сразу передать в буфер обмена или другой команде.

Остальная часть этой статьи посвящена четырём улучшениям, которые превращают эту строку в инструмент, которым действительно удобно пользоваться: shell-функции, корректным кодам выхода, пакетной обработке с ограниченным параллелизмом и буферу обмена. Если вы предпочитаете писать на языке программирования, а не в shell, такой же вызов есть в Python, Go и шести других средах выполнения.

Конвейер в терминале отправляет URL назначения с помощью curl, получает ответ JSON и передаёт его через jq, чтобы вывести короткую ссылку и скопировать её в буфер обмена

Превратите это в команду

У alias не может быть аргумента, поэтому используйте функцию. В .zshrc или .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
}

В этой функции есть три осознанных решения.

Построение JSON с помощью jq -n --arg, а не интерполяция строк, означает, что адрес назначения с кавычкой или амперсандом не сломает запрос. Это shell-аналог параметризованного SQL, и пропуск этого шага приводит к тому же классу ошибок.

--fail-with-body - параметр, о котором часто забывают. По умолчанию curl завершается с кодом 0 при любом завершённом обмене, поэтому ответ 401 проходит через конвейер, jq выводит null, а скрипт продолжает работу с null там, где должна быть ссылка. С этим параметром ответ 4xx устанавливает ненулевой код выхода, а тело ошибки по-прежнему можно прочитать.

Idempotency-Key, полученный из URL, означает, что повторный запуск той же команды вернёт ту же ссылку, а не создаст две. Это особенно важно в следующем разделе о пакетной обработке, а объяснение есть в материале о лимитах запросов и идемпотентности.

Нужен ключ? Создайте его на бесплатном тарифе, экспортируйте из профиля shell, и функция заработает в следующем новом терминале.

Сразу в буфер обмена

Последние несколько символов, которые делают решение действительно завершённым:

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 одновременно выводит ссылку и копирует её, что удобнее, чем копировать вслепую и надеяться на результат.

Пакетная обработка без сбора ответов 429

Четыре улучшения от однострочного curl до удобного инструмента командной строки: shell-функция, явные ошибки, пакетная обработка с параллельным xargs и интеграция с буфером обмена

Файл с URL, по одному в строке, сокращается партиями по восемь адресов:

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

-P 8 - вся стратегия соблюдения лимита запросов: одновременно выполняются восемь запросов независимо от того, содержит файл пятьдесят строк или пятьдесят тысяч. Без этого параметра xargs запускает процессы настолько быстро, насколько может, и API отвечает 429 на большинство запросов.

Перед применением к настоящему файлу стоит знать о двух нюансах. xargs -P перемешивает вывод, поэтому строки могут прийти не по порядку; если соответствие между входом и выходом важно, выводите оба значения:

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

Кроме того, URL с пробелом или кавычкой не переживёт -I{} без экранирования. Для любых пользовательских данных безопасный вариант - xargs -0 с входным файлом, разделённым нулевыми байтами. На этом этапе написать настоящий скрипт на Python обычно проще, чем правильно настроить кавычки.

Не оставляйте ключ в истории

ELIDO_API_KEY=abc123 short <url> помещает ключ в ~/.zsh_history и список процессов, где его сможет прочитать любой другой аккаунт на компьютере с помощью ps.

Экспортируйте его из профиля shell или, что ещё лучше, получайте из хранилища секретов при запуске shell:

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

Тогда для ротации ключа достаточно обновить одну запись, а не искать его по скрытым файлам на трёх компьютерах. Тот же принцип действует на серверной стороне любого планируемого скрипта, а webhooks для событий ссылок помогают получать данные обратно, не оставляя ещё один ключ где-то в системе.

Когда терминал подходит лучше всего

Он подходит, когда URL уже находятся в файле, когда вы уже работаете в shell или когда сокращение - один из шагов более крупного скрипта: процесс выпуска, создающий ссылку на дистрибутив, задание CI, сокращающее URL предварительного развёртывания, git hook, который создаёт ссылку для записи в журнале изменений.

Для управления кампанией терминал подходит плохо. Для папок, тегов и сравнения числа кликов по разным размещениям нужна панель, а попытка делать это с помощью запросов jq к endpoint списка превращается в отдельный проект, а не в быстрое решение. В разделе решений для разработчиков описано, что API предоставляет помимо создания ссылок, а API и SDK рассказывают о типизированных клиентах на случай, когда shell-скрипта становится недостаточно.

Читайте основную серию

Эта статья относится к инженерному кластеру. Сначала прочитайте руководство по бесплатному API сокращателя URL, где описан формат endpoint, затем материал о лимитах запросов и идемпотентности. Актуальная справочная информация находится в документации API.

Материалы по теме

Частые вопросы

Как сократить URL из командной строки?

Отправьте адрес назначения в API сокращателя с помощью curl и передайте ответ через jq, чтобы извлечь короткую ссылку. Ничего не нужно устанавливать, кроме curl и jq, а API-ключ берётся из переменной окружения, поэтому он никогда не появляется в истории shell.

Как превратить это в повторно используемую команду?

Оберните вызов curl в shell-функцию в .zshrc или .bashrc, передавая URL первым аргументом. Перезагрузите профиль, и short https://example.com заработает из любого каталога. Здесь функция лучше alias, потому что ей нужно принимать аргумент.

Почему мой конвейер curl завершается успешно, если API вернул 401?

Потому что curl завершается с кодом 0 при любом завершённом HTTP-обмене, включая ответы с ошибками. Добавьте --fail-with-body, чтобы для ответа 4xx или 5xx устанавливался ненулевой код выхода, а тело ответа по-прежнему выводилось. Иначе скрипт продолжит работу с объектом ошибки там, где должна быть короткая ссылка.

Как сократить целый файл URL из shell?

Передайте файл в xargs с параметром -P 8, чтобы одновременно выполнять восемь запросов, и с -I{}, чтобы подставлять каждую строку. Это ограничивает параллелизм восемью запросами независимо от длины файла и не даёт большой пакетной операции выйти за лимит API и собрать ответы 429.

Как сразу скопировать короткую ссылку в буфер обмена?

Передайте вывод в pbcopy в macOS, xclip -selection clipboard в Linux с X11, wl-copy в Wayland или clip.exe в WSL. Вместе с shell-функцией это означает, что одна команда создаёт ссылку, уже готовую для вставки.

Где должен храниться API-ключ для работы из CLI?

В переменной окружения, экспортируемой из профиля shell, или, что ещё лучше, получайте его при запуске shell из менеджера паролей или хранилища секретов. Если вводить ключ прямо в команду, он попадёт в файл истории и список процессов, где его сможет прочитать любой другой пользователь компьютера.

Попробуйте Elido

Вставьте URL - получите короткую ссылку

Без регистрации. Ссылка живёт 30 дней. Зарегистрируйтесь, чтобы оставить её навсегда.

Бесплатно, без регистрации · 2 в день

Попробуйте Elido

URL-сокращатель с хостингом в ЕС: собственные домены, глубокая аналитика, открытый API. Бесплатный тариф - без банковской карты.

Теги
url shortener cli
shorten url command line
curl url shortener
shorten url terminal
bash shorten link function
xargs parallel api calls

Читать дальше