1分で読了エンジニアリング

URL短縮CLI: ターミナルからリンクを短縮する

curlとjqでコマンドラインからURLを短縮し、シェル関数でラップしてクリップボードにコピーし、xargsでファイル全体を並列処理する方法を解説します。

Marius Voß
DevRel · edge infra
curlでPOSTした結果をjqにパイプし、短縮リンクをターミナルへ直接表示してクリップボードにもコピーするURL短縮CLI

ターミナルからURLを短縮するのは1行でできます。

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から1つのフィールドを取り出し、-rが外側の引用符を外すため、値をそのままクリップボードや別のコマンドへ渡せます。

この投稿では、この1行を実際に使えるものへ変える4つの拡張を紹介します。シェル関数、正しい終了コード、同時実行数を制限した一括処理、そしてクリップボードです。シェルではなく言語で書きたい場合も、同じ呼び出しをPythonGoその他6つのランタイムで利用できます。

curlで遷移先URLをPOSTし、JSONレスポンスを受け取り、jqにパイプして短縮リンクを表示し、クリップボードへコピーするターミナルのパイプライン

コマンドにする

エイリアスは引数を受け取れないため、関数を使います。.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
}

この中には、意図的に3つの選択があります。

文字列補間ではなくjq -n --argでJSONを組み立てるため、引用符やアンパサンドを含む遷移先URLでもリクエストが壊れません。これはシェルにおけるパラメータ化SQLに相当し、これを省くと同じ種類のバグになります。

見落とされがちなフラグが--fail-with-bodyです。デフォルトでは、curlは通信が完了すれば終了コード0になるため、401もパイプを流れ、jqはnullを表示し、スクリプトはリンクがあるはずの場所にnullがある状態で処理を続けます。これを付けると、4xxでゼロ以外の終了ステータスになり、確認するためのエラー本文も取得できます。

URLから生成するIdempotency-Keyにより、同じコマンドを2回実行しても2つのリンクを作成せず、同じリンクが返ります。これは下の一括処理ではさらに重要になります。理由についてはレート制限と冪等性で説明しています。

キーが必要ですか?無料プランで作成し、シェルプロファイルからエクスポートすれば、次に開くターミナルから関数を使えます。

クリップボードへ直接コピーする

最後に、使い勝手を完成させる数文字を加えます。

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を集めずに一括処理する

1行のcurlから実用的なコマンドラインツールへ進む4つの拡張: シェル関数、明確なエラー終了、一括処理とxargsによる並列実行、クリップボード連携

1行に1つずつURLが入ったファイルを、8件ずつ短縮します。

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

-P 8がレート制限対策のすべてです。ファイルが50行でも5万行でも、実行中のリクエスト数は8件に変わりません。これがないと、xargsはプロセスを起動できる限り速く実行し、APIの大半の応答が429になります。

実際のファイルに対して実行する前に、知っておきたい注意点が2つあります。xargs -Pは出力をインターリーブするため、行の順序が入れ替わることがあります。入力と出力の対応関係が重要なら、両方を表示してください。

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

また、スペースや引用符を含むURLは、エスケープしない-I{}では正しく処理できません。ユーザー入力を扱う場合は、NULL区切りの入力ファイルとxargs -0を使うのが安全です。そこまで必要になると、引用符を正しく処理するより、Pythonで専用のスクリプトを書く方が通常は簡単です。

キーを履歴に残さない

ELIDO_API_KEY=abc123 short <url>と実行すると、キーが~/.zsh_historyとプロセス一覧に入り、マシン上の別のアカウントでもpsを使って読み取れる状態になります。

代わりにシェルプロファイルからエクスポートするか、さらに安全性を高めるなら、シェルの起動時にシークレットストアから取得します。

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

キーをローテーションするときも、3台のマシンにあるドットファイルをgrepで探すのではなく、1つのエントリを更新すれば済みます。同じ原則はスケジュール実行するスクリプトのサーバー側にも当てはまり、別の場所にキーを置かずにデータを受け取る方法としては、リンクイベント用のwebhookがそのパターンになります。

ターミナルを使うべき場面

URLがすでにファイルに入っているとき、すでにシェルを開いているとき、または短縮処理が大きなスクリプトの一部であるときは、ターミナルが適しています。たとえば、配布リンクを生成するリリースプロセスプレビュー環境のURLを短縮するCIジョブ、変更履歴のエントリ用リンクを生成するgitフックなどです。

キャンペーンを管理する場所としては適していません。フォルダーやタグの管理、配置ごとのクリック数の比較にはダッシュボードが必要で、リストエンドポイントへのjqクエリで実現しようとすると、ショートカットではなくプロジェクトになります。開発者向けソリューションでは作成以外にAPIが提供する機能を、APIとSDKではシェルスクリプトでは足りなくなったときの型付きクライアントを説明しています。

基幹記事シリーズを読む

この記事はengineeringクラスターに属します。まずはエンドポイントの構造を確認できる無料URL短縮サービスAPIガイドを読み、次にレート制限と冪等性へ進んでください。最新のリファレンスはAPIドキュメントです。

ブログ内の関連記事

よくある質問

コマンドラインからURLを短縮するにはどうすればよいですか。

curlで遷移先URLを短縮サービスのAPIへPOSTし、レスポンスをjqにパイプして短縮リンクを取り出します。curlとjq以外のインストールは不要な1行のコマンドで、APIキーは環境変数から取得するため、シェルの履歴にも表示されません。

再利用できるコマンドにするにはどうすればよいですか。

.zshrcまたは.bashrcのシェル関数にcurlの呼び出しを包み、URLを第1引数として受け取るようにします。プロファイルを再読み込みすれば、どのディレクトリからでも`short https://example.com`で短縮できます。引数を受け取る必要があるため、ここではエイリアスより関数が適しています。

APIが401を返したのにcurlのパイプラインが成功するのはなぜですか。

curlはエラーレスポンスを含め、HTTP通信が完了すれば終了コード0になるためです。--fail-with-bodyを追加すると、4xxまたは5xxでゼロ以外の終了コードになり、本文も表示されます。そうしないと、スクリプトは短縮リンクがあるはずの場所にエラーオブジェクトがある状態で処理を続けてしまいます。

シェルからURLのファイル全体を短縮するにはどうすればよいですか。

ファイルを-P 8付きのxargsへパイプし、8件のリクエストを同時に実行します。-I{}で各行を置き換えます。これによりファイルの長さにかかわらず同時実行数は8件に制限され、大量のバッチ処理が429を集めるのではなく、APIのレート制限内に収まります。

短縮リンクをクリップボードへ直接コピーするにはどうすればよいですか。

macOSではpbcopy、X11を使うLinuxではxclip -selection clipboard、Waylandではwl-copy、WSLではclip.exeへ出力をパイプします。シェル関数と組み合わせれば、1つのコマンドでそのまま貼り付けられるリンクが生成されます。

CLIワークフローではAPIキーをどこに置くべきですか。

シェルプロファイルからエクスポートした環境変数に置くか、さらに安全性を高めるなら、シェルの起動時にパスワードマネージャーやシークレットストアから取得します。キーをコマンドに直接入力すると、履歴ファイルとプロセス一覧に残り、マシン上の別のユーザーが読み取れる可能性があります。

Elidoを試す

URLを貼り付けて短縮リンクを取得

登録不要。リンクは30日間有効。永久に保存するには登録してください。

Free、登録不要 · 1日あたり2件

Elidoを試す

EUホスティングのURL短縮サービス。カスタムドメイン、詳細な分析、オープンAPI付き。無料プラン - クレジットカード不要。

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

続きを読む