Go to list of users who liked
Share on X(Twitter)
Share on Facebook
新しい curl コマンドの使い方 完全ガイド(2025年版)
はじめに
curl とは対話シェルやシェルスクリプトから HTTP 通信を行うのによく使われるコマンドです。あらゆる環境(100 種類の OS)で動作し、macOS や Windows には標準でインストールされています。商用サポートもあり、互換性は非常に重視され、何年経っても同じ書き方で動きます。非常に長く使われており(1998 年生まれの 27 歳1)、そして古い情報もたくさんあります。この記事ではそういった古い情報を、より簡単で新しいcurl コマンドの使い方にアップデートします。最初に結論を書いておくと、
もう-X POST -H "Content-Type: applicatoin/json" なんて書かなくていいですよ。
(記事を読まない人のためのリンク)
この記事を書くにあたって以下の記事を参考にしています。この記事が書かれたのは 2015 年、現在はそれから 10 年後です。
この記事は最新版の新機能の紹介ではなく、以前から使える機能を含むcurl の新しい使い方ガイドです。
curl コマンド入門
まずcurl コマンドを全く知らないという初心者に向けて、簡単に curl コマンドとこの記事の内容について説明します。curl コマンドは、多くの機能を持っておりすべてを把握するのは大変です。この記事では HTTP/HTTPS に関する機能のみを、次の3つに分けて解説しています。このことを意識して読むとcurl コマンドの使い方を理解しやすくなるでしょう。
- GETメソッドと基本の話(ファイルのダウンロードやJSONデータの受信の話)
- HTML フォーム送信の話(
<form>タグからの送信に相当、現在はあまり必要ない) - RESTful API の話(JSON データの送信など、現在の主流の使い方)
RESTful API の使い方が主流となっている現在では、覚えるのが大変な「2. HTML フォーム送信の話」(-d ,--data 系オプションに関する話)をスキップしても(ざっと読むだけも)構わないことを知っておくと少し楽になれます。
cURL (Client for URLs) と curl とは
curl とはコマンドの名前です。cURL(「Client for URLs」の語呂合わせ)はcurl コマンドを作っているプロジェクトの名前です。また各言語用のライブラリの名前 (libcurl) としても使われています。
さてここで一つ、まったく関係のない curl があります。それはプログラミング言語の名前としての curl です。この場合は Curl 言語と書かれることが多いようです。
- https://curl.se/ (本記事で扱っている curl の公式サイト)
- https://www.curl.com/ (本記事とは全く関係ない curl 言語の公式サイト)
どちらも 1998 年頃に、運悪く同じ名前で公開されました。この2つを混同しないように注意してください。この件についての詳細は以下のページ説明されています。
発音 curl は「kurl」です - /kʌrl/ (/kɜrl/)
curl の日本での一般的な読み方は「カール」で良いでしょう。発音の「kurl」は以下のコミットメッセージからです。(コメント より /kʌrl/ (/kɜrl/) )
以前はプロジェクト名「cURL」を決めた理由について「see URL と発音できる」と書かれていたのですが、2021 年 12 月に、私達は決して see URL とは発音しないとして「see URL と読める」に変更したのが上記のコミットです。
個人的には「カール」と読んでいますが... FAQ に実際の発音の音声ファイルがあります。
We pronounce curl with an initial k sound. It rhymes with words like girl and earl. This is a short WAV file to help you:https://media.merriam-webster.com/soundc11/c/curl0001.wav
「くぅろぉ」? (like girl and earl ⋯ girl はガールだからカールでいいよね? )
curl の高い互換性への考え方の紹介
以下のページで curl の互換性についての考え方が紹介されています。正確には ABI (Application Binary Interface) に関する話ですが、コマンドラインインターフェースについても同じと考えて良いでしょう。一言で言えば、互換性絶対破壊しないマンです。
curl は 2023 年 3 月に、バージョン 7.88.1 から 8.0.0 に変更されました(参照)。一般的にメジャーバージョンが更新されるときは、互換性のない変更が含まれることを意味しますが、curl の場合は大きくなりすぎたマイナーバージョン番号をリセットするだけのものです。つまりcurl の 7 系 と 8 系の間に破壊的変更はないので安心して更新できます。
以下の文章は短かったので翻訳してみました。
API 互換性
libcurl は API の安定性を約束し、今日書かれたあなたのプログラムが将来も動き続けることを保証します。私達は互換性を壊すことはありません。
時間とともに、私達は API に機能、新しいオプション、新しい関数を追加しますが、私達は互換性がない方法で動作を変更したり、関数を削除したりしません。
最後に私達が互換性のない方法で API を変更したのは 2006 年の 7.16.0 であり、私達は同じことを行う予定はありません。
他の類似ツールとの比較
機能的な比較一覧が curl 公式サイトにあったので紹介します。ただし情報は少し古いまたは正確ではないかもしれないと思っています。
HTTP/HTTPS に限定して言えば、他のツールも機能的に見劣りしているわけではありません。wget/wget2 は HTML フォームのmultipart/form-data の送信に直接対応してないのがウィークポイントですが、自分でマルチパートデータを組み立てればできないことはないと思います。HTTP PUT には非対応なのは結構ある感じですが、wget/wget2 に関しては随分と前から--method と--header オプションに対応しているのでできる気がします。
もっとも個人的に curl が素晴らしいと思っている点は、どの環境でも動くことと互換性の高さ(バージョンアップは機能が増えるだけで壊れない)を何年もの間実現し続けていることなので、私はそれほど他のツールには興味を持っていません。標準でインストールされている可能性があるwget(GNU 版だけでなくサブセットの BusyBox 版もある) とfetch (FreeBSD) をダウンロード用に使うぐらいです。しかし NetBSD や OpenBSD には標準インストールのダウンロードツールはなく、いずれにしろ何かをインストールしなければならないのなら curl を選択することで環境依存のないどこでも動くシェルスクリプトが書けます。curl、wget、fetch、全てに対応するコードを書く必要はありませんし、wget やfetch で機能が使えないからと言って全てで使える最低限の機能だけで頑張る必要もありません。オープンソースでユーザー数も多いので将来使えなくなるという心配もありません(元の開発者が続けられなくなっても誰でも後を引き継げる)。自作を含めユーザー数が少ないコマンドは長期持続性がなく、バグがあったり将来使えなくなるリスクが高いので継続した開発とユーザー数は重要です。そういう意味では、wget/wget2 や HTTPie は十分良い選択肢です。
今回使用した curl のバージョン (8.12.1)
2025 年 2 月 13 日にリリースされたcurl 8.12.1 です。(記事執筆時点の最新版)
$curl--versioncurl 8.12.1 (x86_64-apple-darwin22.6.0) libcurl/8.12.1OpenSSL/3.4.1 (SecureTransport) zlib/1.2.11 brotli/1.1.0zstd/1.5.6 AppleIDN libssh2/1.11.1 nghttp2/1.64.0 librtmp/2.3Release-Date: 2025-02-13Protocols: dict file ftp ftps gopher gophers http https imap imapsipfs ipns ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftpsmb smbs smtp smtps telnet tftp ws wssFeatures: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxyIDN IPv6 Kerberos Largefile libz MultiSSL NTLM SPNEGO SSLthreadsafe TLS-SRP UnixSockets zstdちなみに上記のProtocols: はcurl が対応しているプロトコルです。ビルドオプションによって変わり、Windows 標準インストール版はいくつかの機能が無効になっています。curl は HTTP 関連だけではなく、ネットワークに関する多くの通信に対応しています。例えば TELNET プロトコルにも対応しているので、curl コマンドはtelnet コマンドがインストールされていない環境での最終手段です。
PowerShell 環境ではcurl(やwget)はInvoke-WebRequest コマンドレットへのエイリアスの場合があります。本物のcurl を実行するにはcurl.exe で実行するかRemove-Item alias:curl を実行してエイリアスを削除してください。
最新版バイナリのダウンロード
各種 OS 用バイナリのダウンロードはこちらから
- https://curl.se/download.html 総合ページ
- https://curl.se/windows/
- curl 公式版 Windows バイナリ
- Windows 標準版とは異なり(おそらく)すべての機能が有効
- https://github.com/stunnel/static-curl/releases
- Linux、macOS、Windows 版バイナリ
- 新しいバージョンが使えない人用のスタティックバイナリ
- curl の総合ページからリンクされているので比較的安心なのでは?
その他に、本当に多種多様な OS 用のバイナリが提供されています。
各 OS のバージョン情報
参考として、curl のバージョンと 各 OS のバージョン情報です。この記事ではいくつかの新しい機能を説明していますが、現時点では標準で使えない環境があるようです。最新版をインストールしましょう。なお、curl の 7 系 と 8 系の間に破壊的な変更はなく、単に増えすぎたバージョン番号をリセットしただけとのことです。
詳細:https://curl.se/docs/releases.html より
この記事で解説している比較的新しいオプションを含むバージョンの抜粋
| curl | 追加オプション または OS (curl) のバージョン |
|---|---|
| Windows 10 1803 (7.55.1) 初搭載, Windows 11 初板 2021年10月 (7.55.1) Ubuntu 18.04 (7.56.0) | |
| 7.61.0 | [2018-07]--oauth2-bearer の HTTP 対応 |
| AlmaLinux 8.10 (7.61.1) | |
| 7.63.0 | [2018-12]--write-out "%{stderr} %{stdout}" |
| 7.67.0 | [2019-11]--no-progress-meter |
| Ubuntu 20.04 (7.68.0), AlmaLinux 9.5 (7.76.0), Ubuntu 22.04 (7.81.0)、Windows 10 / 11 2022年1月頃 (7.79.1) | |
| 7.82.0 | [2022-03]--json |
| macOS 13.0 (7.84.0), macOS 13.1 (7.85.0), macOS 13.2 (7.86.0) Windows 10 / 11 2022年7月頃 (7.83.1) | |
| 7.87.0 | [2022-12]--url-query |
| macOS 13.3 (7.87.0), macOS 13.4 (7.88.1), Windows 10 / 11 2023年4月頃 8.0.1, macOS 13.5-14.1 (8.1.2) | |
| 8.3.0 | [2023-11]--variable,--expand-*,--write-out %output{} |
| macOS 14.2-14.4 (8.4.0), Windows 10 / 11 2023年11月頃 (8.4.0), Ubuntu 24.04 (8.5.0), macOS 14.5 (8.6.0), macOS 14.6 - 15.2 (8.7.1) Windows 10 / 11 2024年5月頃 (8.7.1), Windows 10 / 11 2024年10月頃 (8.9.1) | |
| 8.10.0 | [2024-09-11]-vv,-vvv,-vvvv |
| Windows 10 / 11 2025年2月時点 (8.10.1) | |
| 8.12.0 | [2025-02-05]--variable "name[0-99]@filename" |
| Ubuntu 25.04(予定)(8.12.1) |
- macOS のバージョンは以下より
- Windows のバージョンは以下およびネット上のランダムな情報を参照しています
- https://www.pg-fl.jp/doscmd/curl.htm
- 更新プログラムによってアップデートされるため OS のバージョンに紐づいていません
【参考】 jq のインストール
jq はcurl にとって必ずしも必要なものではありませんが、RESTful API を呼び出そうっていうのなら、もはや必須の道具ですよね? jq の実装も本家あわせて私が知る限り 3 つあります。curl と同じようにどの環境でも動くツール兼言語です。
- 本家https://jqlang.github.io/jq/ 1.7.1 (2023-12-13) C 製
- バイナリのダウンロードはこちら
- Linux、macOS、FreeBSD、Solaris、Windows 用のバイナリが提供されています
- その他の環境でもパッケージ管理システムやソースコードからインストールできます
- https://github.com/itchyny/gojq v0.12.17 (2024-12-01) Go 製
- https://github.com/01mf02/jaq 2.1 (2025-01-15) Rust 製
動作確認方法
まず最初にこの記事で使用している動作確認方法について説明します。この記事ではcurl コマンドがどのようなデータを送信しているかを確認するために、次のnc コマンドを使った簡易サーバーで実際に送信した通信内容を出力しています。
sh-c'while command -p nc -l localhost 8080; do sleep 0.2; done' &この記事でcurl コマンドが出力しているようなログは、実際にはnc コマンドが出力していることに注意してください。
$sh-c'while command -p nc -l localhost 8080; do sleep 0.2; done' &[1] 95576 ← [1] はジョブ番号$curl-sS localhost:8080GET / HTTP/1.1 ┐Host: localhost:8080 │User-Agent: curl/8.12.1 ├ 実は nc コマンドの出力Accept: */* │ ┘^C ← curl が止まるので CTRL+C で止める$kill %1 ← nc コマンドを止めたい場合はジョブ番号を指定してkillする[1]+ Terminated sh -c 'while command -p nc -l localhost 8080;dosleep0.2;done'ちなみに無限ループしているのは、curl コマンド送信を CTRL+C で止めたときにnc コマンドも終了してしまうからです。nc コマンド(環境によってはnetcat)の引数は実装によって違うようなので動かなかったら調べてください。command -p はほとんどの人は書く必要はないのですが、私は Homebrew で別のnc コマンドをインストールしているので OS 標準版を呼び出すために使っています。Ubuntu と macOS はこれで動きました。Windows のやり方は調べていません。
もう一つの動作確認方法として、URL にfile:/dev/null (Windows ではfile:/NUL:)を指定している箇所があります。これは file プロトコルでローカルの/dev/null ファイル、つまり空ファイルを読み込んで出力しており何も出力されません。たしか昔は URL を省略できたと思うのですが今はできないのでその代わりです。
$curl-w'Hello World\n' file:/dev/nullHello Worldこれでどこかのサーバーにあまり負荷をかけることなくデータのみのシンプルなログが得られます。まあ、一部はhttp://example.com にアクセスしてるんですが。この機能curl コマンド標準でできませんかね?
追記 上記の「たしか昔は URL を省略できた」という話は、おそらくCurl 8.6.0+ doesn't allow empty string in args で、空の URL が指定できなくなったというのが正しいようです。元々文書化されていなかった動作が修正された形です(文書化されていない動作とは言え互換性破壊されとるんやが……)。
トレース表示ツール curl-trace
この記事を書いた後に思いついたのでこの記事では使っていませんが、こういうのを作ってみました。
#!/bin/shcurl--no-progress-meter-o /dev/null--trace -"$@" |(set--awk typegawk>/dev/null 2>&1&&set-- gawk-nLC_ALL=C"$@"' /^[0-9a-f]+:/ { $0 = substr($0, 7, 47) for (i = 1; i <= NF; i++) { printf "%c", int("0x" (last = $i)) } } /^== Info: .* sent off/ { if (last != "0a") print "" print "========================================" } /^<= Recv data/ { exit } ')引数はcurl コマンドへの引数そのままです。
$curl-trace example.comGET / HTTP/1.1Host: example.comUser-Agent: curl/8.12.1Accept: */*========================================HTTP/1.1 200 OKContent-Type: text/htmlETag: "84238dfc8092e5d9c0dac8ef93371a07:1736799080.121134"Last-Modified: Mon, 13 Jan 2025 20:11:20 GMTCache-Control: max-age=2962Date: Fri, 21 Feb 2025 11:07:37 GMTContent-Length: 1256Connection: keep-aliveこういうのでいいんだよ、こういうので
仕組みは--trace で通信データが 16 進数表記で出力されるので、それを元に戻しつつ必要な所だけ出力しています。この機能 curl コマンド標準でできませんかね?(2 回目)
-M (--manual), -h (--help) でドキュメント
この記事で解説している curl コマンドの機能やオプションはすべてではありません。本当に正しい情報やすべてを知るには公式のドキュメントを参照してください。通常はman curl で良いのですが、最新バージョンをインストールしたときなどで適切なドキュメントが参照できず困ることがあります。例えば Homebrew 版は諸事情(?)でインストールしても、インストールしたバージョンのマニュアルが参照できません。正しく設定すればよいのですが、それよりも簡単な方法が-M オプションを使用する方法です。この機能は 1998 年の 5.2 から実装されているようです。Windows 標準版ではこの機能は無効にされているようなので公式版をダウンロードしてください。この方法なら実行しているcurl のバージョンのドキュメントを簡単に参照できます。
$curl-M | less特定のオプションのドキュメントだけを見たい場合、-h (--help) オプションが便利です(8.10.0 以降)。
$curl-h-o | lessこれらのオプションについては開発者ブログのこちらの記事もどうぞ。
その他のリンクです。
- https://curl.se/docs/optionswhen.html いつオプションが使えるようになったか
- https://curl.se/docs/manpage.html ウェブ上の manpage
- https://everything.curl.dev/ Everything curl - 公式のオンライン curl 本(PDF版もあり)
- https://curl.se/docs/url-syntax.html URL パーサーの仕様について(自分用メモ)
オプションの数
すべてのオプションはcurl --help all で出力できるので、すべてのオプションの数を数えるのは簡単です。
$curl--help all --abstract-unix-socket <path>Connect via abstract Unix domain socket --alt-svc <filename>Enable alt-svc with this cache file --anyauth Pick any authentication method -a, --append Append to target file when uploading ︙$curl--help all |wc-l267設定ファイル (.curlrc, --config, --disable)
curl コマンドは起動時に読み込む設定ファイル(config ファイル)を持っています。config ファイルはcurl コマンドのオプションに相当のものを記述した設定ファイルです。デフォルトではホームディレクトリの.curlrc が(あれば)読み込まれます(その他のデフォルトの config ファイルについてはドキュメント参照)。
config ファイルは--config オプションで指定できますが、指定するとデフォルトの.curlrc の読み込みが無効になるのではなく追加で読み込まれます。もしデフォルトの config ファイルの読み込みを無効にしたいのであればcurl コマンドの最初の引数として--disable オプションを指定します。シェルスクリプトの場合、config ファイルの読み込みを無効にしておけば、curl コマンドの動作がユーザーの設定に依存しなくなります。config ファイルの書式については--config オプションのドキュメントを参照してください。
ちなみになぜ--disable オプションが最初の引数に限定されているかというと、オプションを設定する設定ファイルは、このような変な仕様を作らないと上手くいかないからです(設定ファイルの中で--disable オプションが指定された場合とか。この問題は実際に実装してみると気づきます……)。
GETメソッドと基本の話
ファイルのダウンロードなど、基本的な GET の話はほとんど変わっていません。GET は「HTML フォーム送信の話」と「RESTful API の話」にも関係してきますが、ここでの話は HTML のリンクをクリックする操作に相当するものと考えると理解しやすくなります。ダウンロードはリンクをクリックするだけでも行われますよね?
URLはクォートしましょう
文字が長くなるのでこの記事では省略していますが、(シェルのメタ文字が含まれるのであれば)URL はクォートしたほうが良いです。
# &はシェルのメタ文字なのでそのまま書けない。?も場合によってはまずい。curl localhost:8080?key1=value1&key2=value2# クォートするのが安全curl'localhost:8080?key1=value1&key2=value2'わかっている人なら構わないのですが、シェルの文法がよくわからないとか言っている人は素直にクォートしましょう。
あと、余談ですが Windows のバッチファイルでは% は%% と書かないとダメとか。面倒なので確認していません。
よく使う -sSfL オプション
オプションはたくさんあります。オプション一覧 が欲しい方はドキュメントを参照するとよいでしょう。ここでは例のいつものよく使うオプションについて説明します。
| オプション | 意味 |
|---|---|
-s,--silent | プログレスメーターや、エラーや警告を出力しない |
-S,--show-error | エラーを出力するが、警告は出力するようにはならない |
-f,--fail | HTTP レスポンスコードが 400、500 番台のときにエラーにする |
-L,--location | リダイレクトを自動的に追跡する |
プログレスメーターだけを非表示にする (--no-progress-meter)
-s (--silent) オプションは、何も出力しないオプションで、プログレスメーターだけでなくエラーも警告も出力されなくなります。ただし-S オプションを付けるとエラーは出力されます。しかし警告は出力されません。プログレスメーターだけを消したい場合には、2019 年の 7.67.0で 新たに--no-progress-meter オプションが追加されています。つまり-sS は--no-progress-meter に置き換えられるということです。長すぎるので、対話シェルではエイリアスを設定し、シェルスクリプトでは次のようなシェル関数を定義してデフォルトオプションを追加するとよいでしょう。
# curl コマンドへデフォルトのオプションを追加するcurl(){commandcurl--no-progress-meter"$@"}# 以下のように実行するだけで上記のオプションが追加されるcurl example.com--silent については開発者ブログのこちらの記事もどうぞ。
ちなみに ootw とはOption-Of-The-Week の略とのことです。一般的には「Outfit Of The Week」の略で、1 週間のお気に入りファッションコーディネートを紹介する時に使われる用語らしいです。
通信データを圧縮する (--compressed)
見逃されがちなのが、curl の通信は圧縮されていないということです。--compressed オプションを指定することで圧縮されます。(補足: 以下の出力は読みやすいように不要な行を削っています。)
$curl-sS-v-o /dev/null example.com>GET / HTTP/1.1>Host: example.com>User-Agent: curl/8.12.1>Accept:*/*< HTTP/1.1 200 OK< Content-Type: text/html< ETag: "84238dfc8092e5d9c0dac8ef93371a07:1736799080.121134"< Last-Modified: Mon, 13 Jan 2025 20:11:20 GMT< Cache-Control: max-age=1262< Date: Thu, 20 Feb 2025 11:44:57 GMT< Content-Length: 1256 ← 元のサイズ< Connection: keep-alive$curl-sS-v-o /dev/null--compressed example.com>GET / HTTP/1.1>Host: example.com>User-Agent: curl/8.12.1>Accept:*/*>Accept-Encoding: deflate,gzip ← 圧縮を受け付ける< HTTP/1.1 200 OK< Accept-Ranges: bytes< Content-Type: text/html< ETag: "84238dfc8092e5d9c0dac8ef93371a07:1736799080.121134"< Last-Modified: Mon, 13 Jan 2025 20:11:20 GMT< Vary: Accept-Encoding< Content-Encoding: gzip ← 圧縮されている< Content-Length: 648 ← サイズが減っている< Cache-Control: max-age=1128< Date: Thu, 20 Feb 2025 11:46:23 GMT< Connection: keep-alive-k (--insecure) オプションを使わない! 証明書エラーを無視しない!
$\mathtt{\huge{危険な‐kオプションは使ってはいけません}}$
-k は kiken の略です。説明を読まない人のためにはこの一言だけで十分でしょう。正しい対処法は「サーバー側の設定を正しく行う」または「自分の環境の設定を正しく行う」です。時計がずれていたり OS が古かったりするのが原因なのでアップデートしましょう。
わかっている人にとってはあたり前のことなのでしょうが、なんの説明もなしに危険な-k オプションを便利だよと紹介する記事が多すぎます。読まない人は読まないですからね。だから読まない人のために書きました。どうしても使用する場合は代わりに‐‐insecure オプションと書きます。「危険」という意味の分かりやすい別名です。対話シェルで時間がない時に使うことはあっても、シェルスクリプトの中に書くことはないはずです。ましてや本番環境では使用しません。
ちなみに、以下のようなエラーが出るときの話です。他にもあるかもしれません。
- curl: (51) SSL: no alternative certificate subject name matches target host name
- curl: (51) SSL: certificate subject name does not match target host name
- curl: (58) Unable to set private key file: wrong pass phrase?
- curl: (59) Could not load client certificate, OpenSSL error
- curl: (60) SSL certificate problem: self signed certificate
- curl: (60) SSL certificate problem: certificate has expired
- curl: (60) SSL certificate problem: certificate is not yet valid
- curl: (60) SSL certificate problem: unable to get local issuer certificate
- curl: (60) SSL certificate problem: unable to verify the first certificate
- curl: (60) SSL certificate problem: invalid CA certificate
- curl: (60) SSL certificate problem: certificate has been revoked
- curl: (77) Problem with the SSL CA cert (path? access rights?)
- curl: (77) error setting certificate verify locations:
- curl: (78) SSL: certificate revocation check failed
この記事を書いている最中、開発者ブログを読んでいたら以下の記事を見つけたので、やっぱ開発者も同じことを思っているんだなぁと思いました。
この記事によると、-k オプションを使用すると、curl はサーバーの以下の TLS 証明書の検証をスキップします。
- 証明書が信頼された認証局(CA)によって署名されているか
- 接続先のホスト名と証明書が一致しているか
- 証明書の有効期限が切れていないか
またlibcurl を使用する場合、以下の値を0 (false) にしてはいけません。
CURLOPT_SSL_VERIFYHOSTCURLOPT_SSL_VERIFYPEER
このオプションは以下の状況下でデバッグ目的で一時的に利用するためです。
- サーバーの証明書を検証するための適切な CA 証明書をまだ持っていない
- サーバーが誤った設定になっており、正規のチェックに失敗してしまう
なぜわかりにくい-k オプションが削除されずに残されているのか? それが curl の高い互換性の考え方だからです。-k オプションを非推奨にし将来削除して--insecure だけにすべきであるという提案は、意図的に壊す提案だとして却下されています。つまり残る手段は私達が-k オプションを使わないことしかないということです。
ファイルに出力する -o, -O オプション
-o (--output) オプションで指定したファイルに保存できます。-O (--remote-name) オプションだと、指定した URL のファイル名部分のみが使われます。
$curl-o index.html http://example.com$curl-O http://example.com/file.tar.gz-o オプションを指定しない場合、標準出力に出力されますが、これを出力したくない場合にはファイル名に/dev/null を指定します。
$curl-o /dev/null http://example.comちなみにリモートが指定する名前を使用したい場合には-J (--remote-header-name) オプションを指定します。この場合、既存のファイルは上書きされません(されたら危険ですね)。上書きを許可する--clobber オプションもあるようですが。あまり使わないほうが良いでしょう。
クエリー文字列を組み立てる (--url-query)
クエリー文字列を組み立てるときには、2022 年のバージョン 7.87.0 で追加された--url-query を使用します。これは昔の-G と-d を併用した回避策を置き換え、従来はできなかった POST などと組み合せて使える方法です。キーはそのままですが値は URL エンコードされます。
$curl-sS--url-querykey1=value1--url-querykey2=value2 localhost:8080GET /?key1=value1&key2=value2 HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*$curl-sS--url-querykey=value-ddata=value localhost:8080POST /?key=value HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*Content-Length: 10Content-Type: application/x-www-form-urlencodeddata=value$curl-sS--url-querykey=値 localhost:8080GET /?key=%e5%80%a4 HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*-i (--include) でレスポンスヘッダを出力
-i (--include) オプションを使用すると、レスポンスのボディの前にレスポンスヘッダを含めて出力します。レスポンスヘッダとレスポンスのボディは間には空行が挟まり区別できます。最近--show-headers という意図が明確な別名が与えられました。
$curl-i amazon.comHTTP/1.1 301 Moved Permanently ┐Server: Server │Date: Thu, 20 Feb 2025 11:14:11 GMT │Content-Type: text/html ├ レスポンスContent-Length: 163 │ ヘッダConnection: keep-alive │Location: https://amazon.com/ ┘ 空行<html>┐<head><title>301 Moved Permanently</title></head> │<body>│<center><h1>301 Moved Permanently</h1></center> ├ レスポンス<hr><center>Server</center> │ ボディ</body>│</html>┘この情報を使ってシェルスクリプトで分岐処理を行えます。もっともこんなことをやっているコードを見たことがありませんが。
CR=$(printf'\r')# ヘッダの改行コード「CR LF」の CR を消すための準備curl-sS-i example.com |{read-r http_version http_code message# ヘッダの1行目の取得whileIFS=read-r line;do# 空行まで読み飛ばすループ["${line%"$CR"}"]||break# CRを削除した行が空行の場合にループを抜けるdone case$http_codein2*)cat;;# 200番台なら標準出力に出力*)cat>&2;;# それ以外なら標準エラー出力に出力esac}HTTP ステータスコード程度なら問題有りませんが、その他ヘッダは解析が面倒なので、-w の出力をレスポンスボディ前に行うやつが欲しい所です。なお、-i はレスポンスボディの一部のように出力されるため、-o で指定した出力先に出力されます。ちなみにレスポンスヘッダのみを見たい場合は、-D - -o /dev/null を使います。-I はレスポンスヘッダを見るオプションではないので気をつけてください。
# 出力を /dev/null に捨てるとレスポンスヘッダも出力されないcurl-sS-i-o /dev/null example.com# レスポンスヘッダのみを見る正しい方法curl-sS-D --o /dev/null example.com-w (--write-out) で追加の情報を出力
-w オプションを指定すると追加の情報を出力に加えます。-i オプションとは反対にレスポンスボディの後に出力されます。どのような情報を参照できるかは、%{json} と%{header_json} を出力するのが簡単です。そのままでは見づらいのでjq コマンドに通すと良いでしょう。補足ですが、jq . の後ろの. は今は不要です。
$curl-sS-w'%{json}'-o /dev/null http://example.com | jq ︙ 長いので省略$curl-sS-w'%{header_json}'-o /dev/null http://example.com | jq ︙ 長いので省略知りたい項目名がわかったら書式を指定して出力すれば OK です。HTTP ヘッダの情報は%header{キー} で指定します。
$curl-sS-w'%{method}: %{url}\n'-o /dev/null http://example.comGET http://example.com$curl-sS-w'%header{content-length}\n'-o /dev/null http://example.com1256-w はデフォルトでは標準出力に出力しますが、バージョン 8.3.0 以降では出力先を標準エラー出力(%{stderr})に切り替えたり標準出力(%{stdout})に戻したりできます。またファイル(%output{ファイル名})に出力したり、追記(%output{>>ファイル名})もできます。
$curl-sS-w'%{stderr}stderr\n%{stdout}stdout\n' file:/dev/null>/dev/nullstderr$curl-sS-w'%{stderr}stderr\n%{stdout}stdout\n' file:/dev/null 2>/dev/nullstdout$curl-sS-w'%output{/tmp/output.txt}Hello\n' file:/dev/null$cat /tmp/output.txtHello$curl-sS-w'%output{>>/tmp/output.txt}World\n' file:/dev/null$cat /tmp/output.txtHelloWorld余談ですが、個人的にはファイルディスクリプタに出力できるようにして欲しくて、ほとんどの環境では/dev/fd/N が実装されているので代わりに使えるのですが、こういう事ができます。こんなコードを書こうという人はあまりいないかもしれませんが(提案しようかな?)。
#!/bin/sh{http_code=$({format='%output{/dev/fd/3}%{http_code}' curl-sS-w"$format"'http://example.com'>&4 4>&-} 3>&1)} 4>&1# http_code が取れる!echo"$http_code"以下のように一時ファイルに出力すれば、上記のコードでやりたいことと同じことができますが、ファイルディスクリプタなら一時ファイルが不要です。
http_code=$(curl-sS-w'%{http_code}'-o /tmp/index.html example.com)# http_code が取れる!echo"$http_code"変数と変数展開機能 (--variable, --expand-*)
2023 年 11 月にリリースされた 8.3.0 から使えるようになった非常に便利な機能が「変数」 です。これは--variable を使ってcurl コマンドだけで使える変数を定義できます。変数を展開したい場合は、--expand- を頭につけてオプションを指定します。つまり--url オプションはデフォルトでは変数は展開されませんが、展開させたい場合は--expand-url という形で指定します。少々面倒な使い方ですが互換性を保つためでしょう。
$curl-sS--variablepath=web--expand-url'localhost:8080/{{path}}'GET /web HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*この変数は頭に% を指定することで環境変数を取り込めます。環境変数が定義されていない場合はエラーになりますが、定義されていないときのデフォルト値も設定すればエラーになりません。
$curl-sS--variable'%USER'--expand-url'localhost:8080/{{USER}}'GET /koichi HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*$curl-sS--variable'%name=guest'--expand-url'localhost:8080/{{name}}'GET /guest HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: *$name=ken curl-sS--variable'%name=guest'--expand-url'localhost:8080/{{name}}'GET /ken HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*普通にシェル変数でいいじゃないか? と思っている頃だと思いますが、便利な機能の一つが起動時の設定ファイル(.curlrc など)で利用できることです。また{{v:url}} みたいな感じで関数を使用できます。関数は{{v:trim:url}} みたいにして複数組み合せられます。現在、定義されている関数は少ないですが、もっと拡張されていくでしょう。なお変数はもう一つ素晴らしい使い方があるのですが、それは後ほど。
設定ファイルで環境変数を取り込む
variable = %USERexpand-write-out ="Hello {{USER}}\n"$curl-sS-o /dev/null example.comHello koichiURLエンコードするだけ {{v:url}}
おまけでjq コマンド版も紹介します。
$curl--variablev='あいおえお'--expand-write-out'{{v:url}}\n' file:/dev/null%E3%81%82%E3%81%84%E3%81%8A%E3%81%88%E3%81%8A$jq-rn--arg v あいうえお'@uri "\($v)"'%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8ABase64エンコードするだけ {{v:b64}}
おまけでjq コマンド版も紹介します。
$curl--variablev='あいおえお'--expand-write-out'{{v:b64}}\n' file:/dev/null44GC44GE44GK44GI44GK$jq-rn--arg v あいうえお'@base64 "\($v)"'44GC44GE44GG44GI44GKJSONエスケープするだけ {{v:json}}
おまけでjq コマンド版も紹介しますが、少し出力が異なります。
$curl--variablev=' \ " '--expand-write-out'{{v:json}}\n' file:/dev/null \\ \"$jq-rn--arg v' \ " ''@json "\($v)"'" \\ \" "trimするだけ {{v:trim}}
$curl--variablev=' hello '--expand-write-out'[{{v:trim}}]\n' file:/dev/null[hello]変数の内容をファイルから読み込む
変数の内容をファイルから読み込むこともできます。
$catfile.txtupdownleftright$curl--variable v@file.txt--expand-write-out'[{{v:trim}}]\n' file:/dev/null[updownleftright]読み込む位置をバイト単位で範囲を指定して読み込めます。
$curl--variable v[6-10]@file.txt--expand-write-out'[{{v}}]\n' file:/dev/null[down ]$curl--variable v[6-10]@file.txt--expand-write-out'[{{v:trim}}]\n' file:/dev/null[down]ファイルの中から特定の範囲のデータを取り出すというユースケースが謎でしたが、ファイルは固定長ファイルで空きはスペースで埋められていると考えれば、もう一つのユースケースが謎だった trim の使い道が見えてくるわけで・・・うーん? 行番号指定での読み込みとかで良くない?
追記コメント より、ユースケースは分割アップデートではないかと。なるほど。
HEADメソッドの話
HEAD メソッドとは、ウェブサーバーに接続し「本文は返さなくて良い。ウェブページが更新されているかを知りたいだけなんだ。ヘッダ情報だけ返してくれ。」と問い合わせる時に使う HTTP メソッドです。本文を返さないためサーバーの負荷やネットワーク通信量を減らす素晴らしい機能ですが、おそらくcurl コマンドの通常の利用者が使うことはあまりないでしょう。
-I(大文字アイ)はレスポンスヘッダを見る方法ではない
非常に多く見かける間違いなのですが、-i(小文字のアイ)オプションと-I(大文字のアイ)オプションは全く違う機能です。出力にレスポンスヘッダを含める-i (--include) に対して、-I (--head) オプションは HEAD メソッドを実行するオプションです。実行している HTTP メソッドが違います。GET メソッドのレスポンスヘッダを見たい時に HEAD メソッドを実行したらだめでしょう? まあほとんど GET と同じようなレスポンスヘッダを返すのですが、-I では POST などのレスポンスヘッダは見られません。ちゃんと説明するなら-I は「HEAD メソッドのレスポンスヘッダ」を出力するオプションです。
$curl-I localhost:8080HEAD / HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*-I オプションを指定するとレスポンスボディではなくレスポンスヘッダを出力するという動作に切り替わったり、-X オプションで HTTP メソッドを上書きできるようなのでレスポンスヘッダを見られないこともなかったりと、微妙な仕様があり、それが勘違いしてしまう理由だと思いますが、レスポンスヘッダを見るなら-D オプションか-v オプションが適切です。
追記-I (HEAD) と-D (GET) で違いがあるサイトないかなーと探していたら灯台下暗しというやつで、ずっと使っていた example.com が出力が違っていました。HEAD はレスポンスボディがいらないので、まあそうなるわなって感じですね。
$curl-sS-I example.comHTTP/1.1 200 OKContent-Type: text/htmlETag: "84238dfc8092e5d9c0dac8ef93371a07:1736799080.121134"Last-Modified: Mon, 13 Jan 2025 20:11:20 GMTCache-Control: max-age=1490Date: Sat, 22 Feb 2025 11:38:41 GMTConnection: keep-alive$curl-sS-D --o /dev/null example.comHTTP/1.1 200 OKContent-Type: text/htmlETag: "84238dfc8092e5d9c0dac8ef93371a07:1736799080.121134"Last-Modified: Mon, 13 Jan 2025 20:11:20 GMTCache-Control: max-age=1036Date: Sat, 22 Feb 2025 11:38:47 GMTContent-Length: 1256 ← Content-Length が出力されているConnection: keep-alive-I オプションをレスポンスヘッダだけを出力するオプションだと思っていると「あれ? examle.com はContent-Length を返さないんだ?」と勘違いしてしまいます。
追記2Cloudfrontのコンテンツ圧縮機能でハマった に-I をレスポンスヘッダの確認機能だと勘違いしていたことによる失敗談を見つけました。こちらではContent-Encoding の出力の有無に差があるようです。
デバッグ方法の話
-D (--dump-header) - レポンスヘッダの内容を確認する
-i と似た機能でこちらもレスポンスヘッダを出力しますが、レスポンスヘッダはレスポンスのボディの出力の一部としてではなく、指定したファイルに出力されます。ただし出力先に- を指定すると標準出力に出力されます。バージョン 8.10.0 から出力先に% を指定すると標準エラー出力に出力されるようになりました。
$curl-sSfL-D --o /dev/null example.comHTTP/1.1 200 OKContent-Type: text/htmlETag: "84238dfc8092e5d9c0dac8ef93371a07:1736799080.121134"Last-Modified: Mon, 13 Jan 2025 20:11:20 GMTCache-Control: max-age=1500Date: Thu, 20 Feb 2025 11:13:47 GMTContent-Length: 1256Connection: keep-alive$curl-sSfL-D %-o /dev/null example.comHTTP/1.1 200 OKContent-Type: text/htmlETag: "84238dfc8092e5d9c0dac8ef93371a07:1736799080.121134"Last-Modified: Mon, 13 Jan 2025 20:11:20 GMTCache-Control: max-age=1514Date: Thu, 20 Feb 2025 11:17:45 GMTContent-Length: 1256Connection: keep-aliveなお-I はレスポンスヘッダを確認する方法ではない ので注意してください。
-v (--verbose, -vv, -vvv, -vvvv) - 通信内容の詳細を確認する
-v オプションを指定すると通信の詳細情報が標準エラー出力に出力されます。バージョン 8.10.0 から-vv、-vvv、-vvvv みたいに書くと詳細レベルが増加し、それまでは--trace-time や--trace-ascii などを指定しなければ見られなかった情報が簡単に見られるようになりました。実際の出力は量が多いので省略します。ぜひ試してみてください。
$curl-sSfL-v-o /dev/null example.com* Host example.com:80 was resolved.* IPv6: (none)* IPv4: 96.7.128.175, 96.7.128.198, 23.192.228.80, 23.215.0.138, 23.192.228.84, 23.215.0.136* Trying 96.7.128.175:80...* Connected to example.com (96.7.128.175) port 80* using HTTP/1.x>GET / HTTP/1.1>Host: example.com>User-Agent: curl/8.12.1>Accept:*/*>* Request completely sent off< HTTP/1.1 200 OK< Content-Type: text/html< ETag: "84238dfc8092e5d9c0dac8ef93371a07:1736799080.121134"< Last-Modified: Mon, 13 Jan 2025 20:11:20 GMT< Cache-Control: max-age=2898< Date: Thu, 20 Feb 2025 11:26:31 GMT< Content-Length: 1256< Connection: keep-alive<{ [1256 bytes data]* Connection #0 to host example.com left intact*補足として-v -v のように分けて書いても詳細レベルは増加しません。これはコマンドラインインターフェースの互換性を保つための機能です(例えば設定ファイル.curlrc でverbose を有効にしてる状態で対話シェルから-v を指定した場合)。詳細は開発者ブログを参照してください。
--trace, --trace-ascii
--trace を指定すると通信内容を 16 進数表記で出力し、--trace-ascii を指定すると ASCII で出力します。出力先はファイルまたは標準出力「-」や標準エラー出力「%」を指定できます。現在は代わりに-vvv などを使ったほうが楽でしょう。
$curl-sSfL--trace --o /dev/null example.com== Info: Host example.com:80 was resolved.== Info: IPv6: (none)== Info: IPv4: 96.7.128.198, 23.215.0.136, 23.192.228.80, 23.215.0.138, 23.192.228.84, 96.7.128.175== Info: Trying 96.7.128.198:80...== Info: Connected to example.com (96.7.128.198) port 80== Info: using HTTP/1.x=>Send header, 75 bytes(0x4b)0000: 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a GET / HTTP/1.1..0010: 48 6f 73 74 3a 20 65 78 61 6d 70 6c 65 2e 63 6f Host: example.co0020: 6d 0d 0a 55 73 65 72 2d 41 67 65 6e 74 3a 20 63 m..User-Agent: c0030: 75 72 6c 2f 38 2e 31 32 2e 31 0d 0a 41 63 63 65 url/8.12.1..Acce0040: 70 74 3a 20 2a 2f 2a 0d 0a 0d 0a pt: */*....== Info: Request completely sent off<= Recv header, 17 bytes (0x11)0000: 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d HTTP/1.1 200 OK.0010: 0a$curl-sSfL--trace-ascii --o /dev/null example.com== Info: Host example.com:80 was resolved.== Info: IPv6: (none)== Info: IPv4: 23.215.0.136, 23.192.228.80, 23.215.0.138, 23.192.228.84, 96.7.128.175, 96.7.128.198== Info: Trying 23.215.0.136:80...== Info: Connected to example.com (23.215.0.136) port 80== Info: using HTTP/1.x=>Send header, 75 bytes(0x4b)0000: GET / HTTP/1.10010: Host: example.com0023: User-Agent: curl/8.12.1003c: Accept: */*0049:== Info: Request completely sent off<= Recv header, 17 bytes (0x11)0000: HTTP/1.1 200 OK認証とセキュリティの話
どのくらいのセキュリティレベルが必要かは、時と場合次第です。いつでも真面目にやる必要はありません。ただし真面目なやり方を知っておく必要はあり、重要な場合には真面目なやり方をする必要があります。
手抜きしても構いませんが、それは手抜きが許されるときだけです。普段は「手抜きでいいじゃないか!」が許されたからといって、それを本番環境で使って良いことにはなりません。手抜きができるのは正しいやり方を知っている人だけであり、正しいやり方を知らないのであれば、それが精一杯ということであり、手を抜いていることにはなりません。
つまり、対話シェルで開発中に行う手抜きと、信頼性が高いシェルスクリプトの書き方は違うという話です。
コマンド引数の秘密情報は漏洩する
この話は公式本の「Everything curl」でも警告されています。
Unix/Linuxに共通する話として、コマンドの引数に書いたパスワードは同じコンピュータを使う他のユーザーから読み取れます。他のユーザーとは root ユーザーのことではありません。他の一般ユーザーです。したがってパスワードをコマンドの引数に書いてはいけません。もちろん HTTP ヘッダに指定するアクセストークンなども同じです。
$curl--user'key:secret' localhost:8080 ↑ 他のユーザーに漏洩しています!$curl-H"Authorization: Bearer YourAccessToken" localhost:8080 ↑ 他のユーザーに漏洩しています!$whoamiguest$ps-a-o user,command |grepcurlkoichi curl --user key:secret localhost:8080guest grep --color=auto curl$ps-a-o user,command |grepcurlkoichi curl -H Authorization: Bearer YourAccessToken localhost:8080guest grep --color=auto curlmacOS ではパスワードが隠されているようですが、これはcurl コマンドによる処理なので一瞬は見えるはずですし、アクセストークンの方は隠されていません。
「自分しかこのパソコンを使ってないからいいじゃないか!」というセリフは、この問題に気づいている人だけが言って良い言葉で、この問題を警告せずにコマンドの引数にパスワードやアクセストークンを指定しているサンプルコードが大量にあるのを見ると、気づいていない人が大半でしょう?
実際の脅威で言えば、学校などで1つのサーバーを複数人で利用している場合や、共有のレンタルサーバーで動かしているシェルスクリプトから、何かしらのコマンドの引数に書いた秘密情報は他のユーザーに漏洩しているということです。ぜひこの話を周りの人に広めてください。
.netrc や config で秘密情報を隠す
対処方法の1つは Everything curl にも書いてあるとおり.netrc ファイルやconfig ファイルを使うことです。.netrc ファイルはログイン名とパスワードを保存するファイルで、curl に限らず、wget やいくつかのネットワークツールなどで使われています。--netrc オプションを指定すればホームディレクトリ以下の.netrc ファイルを参照します。.netrc には複数の接続先情報を書けるのでホームディレクトリで十分だと思いますが、必要な場合は--netrc-file オプションで別のファイルも指定できます。
# localhost に接続するための情報machinelocalhost# 接続先のホスト名loginkey# localhost に接続するときのログイン名passwordsecret# localhost に接続するときのパスワード# 補足: 同じファイルに複数の接続先情報を書けるmachineexample.comloginguestpasswordguest$curl--user'key:secret' localhost:8080GET / HTTP/1.1Host: localhost:8080Authorization: Basic a2V5OnNlY3JldA==User-Agent: curl/8.12.1Accept: */*$curl--netrc localhost:8080GET / HTTP/1.1Host: localhost:8080Authorization: Basic a2V5OnNlY3JldA== ← 一致しているUser-Agent: curl/8.12.1Accept: */*HTTP ヘッダに書くアクセストークンなどでは.netrc は使えないので config ファイル(ファイル名は何でも良い)を使います。接続先ごとに接続情報は異なるはずなので~/.curlrc ではなく--config オプションで指定することになるでしょう。こちらもパーミッションを 600 にして他ユーザーから見えないようにする必要があります。
header = "Authorization: Bearer YourAccessToken"$curl-H"Authorization: Bearer YourAccessToken" localhost:8080GET / HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*Authorization: Bearer YourAccessToken$curl--config myconfig localhost:8080GET / HTTP/1.1Host: localhost:8080Authorization: Bearer YourAccessToken ← 一致しているUser-Agent: curl/8.12.1Accept: */*環境変数で秘密情報を隠す
秘密情報は環境変数を使っても隠すことができます。これがcurl に追加された変数機能のもう一つの素晴らしい使い方です。もちろんシェルの変数機能を使ったらだめです。これは変数展開がコマンド実行の前に行われるからです。
$user='key:secret'$curl--user"$user" localhost:8080$token=YourAccessToken$curl-H"Authorization: Bearer$token" localhost:8080そうではなく、curl コマンドの環境変数の取り込みと変数展開機能を使います。
$user='key:secret' curl--variable'%user'--expand-user'{{user}}' localhost:8080GET / HTTP/1.1Host: localhost:8080Authorization: Basic a2V5OnNlY3JldA==User-Agent: curl/8.12.1Accept: */*$token='YourAccessToken' curl--variable'%token'\ --expand-header 'Authorization: Bearer {{token}}' localhost:8080GET / HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*Authorization: Bearer YourAccessTokenコマンドが長いとクレームが来そうですが、そもそも「Authorization: Bearer ・・・」だって長いわけで、これを省略するconfig を使えばよいのです。秘密情報は環境変数で渡す方法であれば config ファイルの共通化は可能なはずです。また、シェルスクリプトであれば長くても問題ないでしょう。
ここでコマンド名の前の環境変数代入(token='YourAccessToken' curl・・・)は漏れないの?と思うかも知れませんが、この部分はシェルが解釈して実行している部分なので問題ありません。気になるようならexport コマンドで環境変数をエクスポートしても構いませんが、不要なコマンドにまで秘密情報を渡してしまうことになりかねないので注意が必要です。シェルスクリプトであれば一時的に環境変数としてexport した後にエクスポート属性を削除したほうが良いでしょう。なおコマンドの前で設定する環境変数は一時的なものでコマンドを終了した後には残りません。
token='YourAccessToken'# コマンドの前で設定する環境変数は一時的なもの# ただし呼びだすコマンドが「シェル関数」の場合は# 以下のシェルではシェル関数を抜けても環境変数のままなので注意# dash 0.5.9 まで、ksh93、NetBSD ksh、OpenBSD kshtoken="$token" curl--variable'%token'\--expand-header'Authorization: Bearer {{token}}' localhost:8080# export を使う場合は、使用後にエクスポート属性を削除して環境変数ではなくすexporttokencurl--variable'%token'\--expand-header'Authorization: Bearer {{token}}' localhost:8080export-n token# bash でのみ使えるtypeset +x token# bash、ksh93、zsh で使える困ったことに POSIX で標準化されているシェルの機能の範囲では、変数の値を保持したままエクスポート属性のみを削除する簡単な方法がありません。bash などを使えば簡単ですが、どうしても dash などで実現したい人のためにエクスポート属性のみを削除する関数はこちらです。
unexport(){while[$#-gt 0];doeval"set --\"\$$1\"\"\$@\"; unset$1;$1=\$1; shift 2"done}exportvar1=123var2=456unexport var1 var2echo"$var1$var2"# => 123 456 (変数の中身はそのまま)標準入力やファイルで秘密情報を隠す
-H などいくつかのオプションは標準入力からのデータ読み取りに対応しており、標準入力やファイルを使うことでも秘密情報を隠せます。ファイルを使う場合は他の人が読み取れないようにしてください。@-で標準入力、@ファイル名でファイルから読み取ります。
$echo"Authorization: Bearer YourAccessToken" | curl-H @- localhost:8080GET / HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*Authorization: Bearer YourAccessTokenここでecho コマンドの引数は他のユーザーから読み取られることはないのか? と思うかもしれませんが、echo コマンドは事実上すべてのシェルでビルトインコマンドなのでps コマンドなどのプロセス一覧に出現することはありません。ただし POSIX 的にはecho コマンドをビルトインコマンドとして実装することは要求されていないので、可能性としてはecho コマンドが外部コマンドである POSIX シェルの存在はありえます。ちなみにprintf コマンドは多くのシェルでビルトインコマンドですが、OpenBSD sh/ksh と mksh では外部コマンドなので注意してください。
echo コマンドの代わりにヒアドキュメントを使う方法も安全です。これは完全にシェルの機能なのでecho コマンドのような外部コマンドの可能性を心配する必要はありません。
$curl-H @- localhost:8080<<'HERE'Authorization: Bearer YourAccessTokenHEREGET / HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*Authorization: Bearer YourAccessTokenbash、ksh93、zsh などであればヒアストリングから読み取る方もあります。
$curl-H @- localhost:8080<<<"Authorization: Bearer YourAccessToken"GET / HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*Authorization: Bearer YourAccessToken標準入力から読み込む場合の欠点は、標準入力を別の用途(例えば送信データなど)に使えなくなることです。ただし bash、ksh93、zsh などであればプロセス置換で回避可能です。
$curl-H @<(echo"Authorization: Bearer YourAccessToken") localhost:8080GET / HTTP/1.1Host: localhost:8080User-Agent: curl/8.5.0Accept: */*Authorization: Bearer YourAccessTokenプロセス置換の代わりにmkfifo コマンドで作った名前付きパイプを使えばすべての POSIX シェルに対応できますが、名前付きパイプを安全作るにはどうすればいいかなどの話が必要で、ファイルを使う方が簡単なので割愛します。
アクセストークン(Bearerトークン)の指定 (--oauth2-bearer)
アクセストークン(Bearerトークン)は-H (--header)の代わりに--oauth2-bearer でも指定できます。こちらの方がより短く書けます。もちろんここまでの説明通り、--expand-oauth2-bearer '{{token}}' のような環境変数を使った書き方などもできます。
$curl--oauth2-bearer YourAccessToken localhost:8080GET / HTTP/1.1Host: localhost:8080Authorization: Bearer YourAccessTokenUser-Agent: curl/8.12.1Accept: */*$curl-H"Authorization: Bearer YourAccessToken" localhost:8080GET / HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*Authorization: Bearer YourAccessToken--oauth2-bearer は HTTP だけでなく IMAP、LDAP、POP3、SMTP でも共通して使えます。このオプションは 2013 年の バージョン 7.33.0 で追加されました。古くから使えるオプションなのに使用例が少ない気がするのは何でだろう?と思ったら、HTTP で使えるようになったのは 2018 年のバージョン7.61.0 からのようです。
HTML フォーム送信の話
curl コマンドは人がウェブページ、つまり HTML にアクセスするような使い方ができます。すでに説明した単純な GET はリンクをクリックするのと同等の操作です。HTML にはフォーム(<form> タグ)による送信機能があります。フォームの送信からは GET と POST が行えます。ここで説明するのはそのような HTML フォームの操作をcurl コマンドから行う方法です。HTML フォームに関連する HTTP メソッド、つまり GET と POST は、RESTful API よりも数年早くに誕生しました。curl コマンドへの実装も HTML フォームに関連する機能が先行しています。近年の RESTful API の使い方は、HTML フォームの送信とあまり関係ないのですが、HTML フォーム送信の話はcurl コマンドの使い方の基本でもあります。
GET メソッド (-G)
フォームの GET メソッドは HTML で次のように書きます。
<formmethod="GET"action="/get.cgi"><inputtype="text"name="name1"value="値1"><inputtype="text"name="name2"value="値2"><inputtype="submit"></form>これと同じことをcurl コマンドで行う場合には次のように書きます。
$curl-G--data-urlencodename1=値1--data-urlencodename2=値2 localhost:8080/get.cgiGET /get.cgi?name1=%e5%80%a41&name2=%e5%80%a42 HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*-G (--get) オプションを指定しているのがポイントです。これがなければ POST になってしまいます。
次で説明する--data-urlencode を含む、-d (--data) 系オプションは 2000 年台の初期に POST を行うためのオプションとして追加された HTML フォーム(<form> タグの機能)に対応する機能です。HTML フォームは GET も送信でき、その場合は送信するとクエリー文字列に変換されます。この動作をシミュレートしたのが-G (--get) オプションです。<form> タグの場合はmethod 属性を省略したときのデフォルトが GET であるという違いはありますが、-G オプションの役目は-d 系オプションの指定によって POST になったものを GET に戻すことです。-X GET でも GET メソッドで送信できますが、-G (--get) オプションは HTML フォームと関連した機能という考え方が含まれています。詳細は以下の開発者ブログを参照してください。
POST メソッド (--data-urlencode, application/x-www-form-urlencoded)
フォームの POST メソッドは HTML で次のように書きます。
<formmethod="POST"action="/post.cgi"><inputtype="text"name="name1"value="値1"><inputtype="text"name="name2"value="値2"><inputtype="submit"></form>これと同じことをcurl コマンドで行う場合には次のように書きます。
$curl--data-urlencodename1=値1--data-urlencodename2=値2 localhost:8080/post.cgiPOST /post.cgi HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*Content-Length: 33Content-Type: application/x-www-form-urlencodedname1=%E5%80%A41&name2=%E5%80%A42「データを送信した場合は POST であるはずだ」という設計方針なのでしょう。Content-Type がapplication/x-www-form-urlencoded になっているところに注意してください。したがって--data-urlencod を使うのが一番簡単です。--data などを使う場合は自分で URL エンコードする必要があります。curl 組み込みに変数と変数展開機能を使っても URL エンコードできます。--data-urlencode の引数はただの文字列ではなく、ファイルの読み込みなど特殊な書式が使えます。
--data-urlencode <data> | 意味 |
|---|---|
content | 文字列(= や@ が含まれると違う意味になる) |
=content | 文字列(最初の= は無視される) |
name=content | 名前=文字列 形式 |
@filename | 文字列(文字列はファイルから読み込む) |
name@filename | 名前=文字列 形式(文字列はファイルから読み込む) |
便利な機能ですがシェルスクリプトなどでシェル変数をそのまま使用していると、文字列を送信するだけだと思っていたのに、ファイルを送信してしまう可能性があります。
value='@/etc/hosts'curl--data-urlencode"$value" localhost:8080/post.cgi# 代わりにこうするcurl--data-urlencode"=$value" localhost:8080/post.cgiPOST メソッド (-F, multipart/form-data)
フォームの POST メソッドで画像などをアップロードするときには HTML で次のように書きます。書き方が違うのは画像のような大きなデータは URL エンコードして渡すのは適していないからです。
<formmethod="POST"action="/post.cgi"enctype="multipart/form-data"><inputtype="text"name="name1"value="値1"><inputtype="text"name="name2"value="値2"><inputtype="file"name="image"><inputtype="submit"></form>enctype="multipart/form-data" が追加されているのがポイントです。 これと同じことをcurl コマンドで行う場合には次のように書きます。
$curl-Fname1=値1-Fname2=値2-Fimage=@image.png localhost:8080/post.cgiPOST /post.cgi HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*Content-Length: 413Content-Type: multipart/form-data;boundary=------------------------dE72sJJFXYuwGekPOTi67v--------------------------dE72sJJFXYuwGekPOTi67vContent-Disposition: form-data;name="name1"値1--------------------------dE72sJJFXYuwGekPOTi67vContent-Disposition: form-data;name="name2"値2--------------------------dE72sJJFXYuwGekPOTi67vContent-Disposition: form-data;name="image";filename="image.png"Content-Type: image/png(省略)--------------------------dE72sJJFXYuwGekPOTi67v---F (--form) オプションを使用するとmultipart/form-data で送信されます。ちなみに-F オプションと--data 系オプションを併用できない理由は、このように送信形式が異なるためです。filename やContent-Type は次のような書き方で指定できます。
-F'image=@image.png;type=image/jpg;filename=img.jpg'このように、-F オプションの引数はただの文字列ではなく、ファイルの読み込みなど特殊な書式が使えます。つまりシェル変数をそのまま使用すると、ファイルを送信してしまう可能性があります。代わりに--form-string を使えばこのような特殊な書き方が無効になるので適切なオプションを使い分ける必要があります。詳細はドキュメントを参照してください。
name='@/etc/hosts'curl-Fname="$name" localhost:8080/post.cgi# 代わりにこうするcurl--form-stringname="$name" localhost:8080/post.cgi--data 系オプション
--data-urlencode 以外のオプションは URL エンコードを行わないため、通常とは異なるデータの送信に使えます。ただし--data 系オプションはいずれもContent-Type をデフォルトでapplication/x-www-form-urlencoded に設定するため、自分で URL エンコードを行うか、必要な場合は-H (--header) オプションでContent-Type を別のもの変える必要があるでしょう。それぞれの --data 系オプションの違いは次のとおりです。詳細はドキュメントを参照してください。
| オプション | @ファイル 書式 |
|---|---|
-d,--data,--data-ascii | 使える(改行が消える) |
--data-binary | 使える(改行は消えない) |
--data-raw | 使えない(引数の文字列をそのまま送信する時に使う) |
Cookie を使う
注意: ほとんど検証していません。
curl コマンドで Cookie を使う時は、-b (--cookie) <data|filename> オプション(Cookie の送信)や -c (--cookie-jar) <filename> オプション(Cookie の保存)を使うようです。Cookie はファイルに保存することで維持されますが、curl コマンドは 1 回のコマンド実行で複数の URL にアクセスでき、Cookie を保存するファイル名を/dev/null にすることで、メモリ内だけで Cookie を維持できるようです。
$curl-sSFl-c /dev/null localhost:8080/post.cgi localhost:8080/post.cgiCGI 作るのも面倒だったので、どこぞのサンプルサイトで試してみましたが動いているようです。
RESTful API の話
おそらく現在のcurl コマンドのメインの使い方でしょう。前提知識として「認証とセキュリティの話」は読んでおいてください。HTML フォーム送信の話は忘れても良いぐらいです。その方が簡単に使い方を理解できます。
JSON データを送信する (--json, -H)
2022 年のバージョン 7.82.0 で追加された--json オプションで、curl コマンドでの RESTful API の使い方は変わりました。基本として-H (--header) も-d (--data) も使いません。HTML フォーム送信の話は何だったんだ?というぐらい簡単です。基本はこれだけです。
$curl--json'{"key": "value"}' localhost:8080/apiPOST /api HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Content-Type: application/jsonAccept: application/jsonContent-Length: 16{"key": "value"}やることは--json オプションで JSON データを送信するだけです。以前は-H オプションで指定していたContent-Type: application/json とAccept: application/json は自動的に設定されます。--json オプションはこの2つの設定を行い--data-binary オプションを指定することに相当します。したがって POST メソッドであり、内部的には--data-binary オプションと同じなので@ で始めることでファイルや標準入力からの読み込みも行えます。
# ファイルからの読み込みcurl--json @file.json localhost:8080/api# 標準入力からの読み込みcurl--json @- localhost:8080/api < file.json# ヒアドキュメントからの読み込みcurl--json @- localhost:8080/api<<'HERE'{"key": "value"}HERE# パイプからの読み込みecho'{"key": "value"}' | curl--json @- localhost:8080/apiもし 7.82.0 よりも古いバージョンしか使えない場合は、このように書くのと同じです。
$curl-H'Content-Type: application/json'-H'Accept: application/json'\ --data-binary '{"key": "value"}' localhost:8080/apiPOST /api HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Content-Type: application/jsonAccept: application/jsonContent-Length: 16{"key": "value"}もちろん RESTful API だからといって JSON データを送信するとは限らないので、異なる形式のデータを送信する場合は-H (--header) オプションを指定してContent-Type やAccept を変更し(JSON ではないので)--json ではなく--data 系オプションを使うことになるでしょう。
application/json は charset を持たない
Content-Type: application/json にcharset が指定されていないことが気になる人がいるかも知れないので書いておくと、JSON データは RFC 8259 によって BOM なしの UTF-8 でなければならないと規定されており、IANA への登録 にもapplication/json にcharset はありません。したがってcharset を付けても何も効果がなかったり、付けてしまうと不正なContent-Type と解釈され正常に動作しない可能性も考えられます。
内部システムに関してはcharset が必要だったり UTF-8 以外の文字コードを使う、または使える標準に準拠していないシステムがあるかもしれませんが、curl コマンドとしてはcharset は付けてはいけないものであり、どうしても必要な人だけが追加しなければいけないものです。なお、当然のことながらデータが UTF-8 に自動的に変換されるようなことはないので、curl コマンドに渡すデータは UTF-8 でなければなりません。もっとも最近は UTF-8 を基本的に使っているとは思いますが。
JSON データは jq や jo で組み立てよう
curl コマンドの引数の JSON データを手書きするのは面倒ですよね? 例えばキーのダブルクォートとか。それならばjq コマンドを使って書きましょう。もちろんその他のツールでも構いません。
$jq-n'{key: 123}' | curl--json @- localhost:8080/apiPOST /api HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Content-Type: application/jsonAccept: application/jsonContent-Length: 17{ "key": 123}jq コマンドを使った JSON データの書き方の例をいくつか紹介します。
$jq-n' { key: 123 }'{ "key": 123}$jq-nf /dev/stdin<<HERE { key: 123 }HERE{ "key": 123}$jq-n--arg key1 value1--arg key2 value2'$ARGS.named'{ "key1": "value1", "key2": "value2"}$jq-n--arg key1 123--argjson key2 456'$ARGS.named'{ "key1": "123", "key2": 456}他にもjo などで JSON データの組み立ては可能で、もっと簡潔に書くことができます。
$jo-pname=jon=17parser=false{ "name": "jo", "n": 17, "parser": false}$jo-pname=jon=17parser=false | curl--json @- localhost:8080/apiPOST /api HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Content-Type: application/jsonAccept: application/jsonContent-Length: 52{ "name": "jo", "n": 17, "parser": false}jo は Everything curl でも紹介されています。
curl コマンドの JSON データの扱いは面倒かもしれませんが、そもそもcurl コマンドは JSON データ専用に作られているわけではありません。異なる機能を 1 つのコマンドにまとめるとそれはそれで便利なこともありますが、すべてをcurl コマンドだけでやらなきゃいけないってこともないわけで、JSON データの組み立てはそれが得意なjq コマンドやjo コマンドに任せ、組み合わせて使うのが Unix 哲学の考え方です。
PUT でデータをアップロードする (-T)
PUT メソッドでファイルを送信する場合は-T (--upload-file) オプションを使います。
$curl-sSfL-T file.txt localhost:8080/filePUT /file HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*Content-Length: 5test標準入力から読み取る場合は「-」を指定します。この場合 chunk で送信されるようです。
$curl-sSfL-T - localhost:8080/file < file.txtPUT /file HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*Transfer-Encoding: chunkedExpect: 100-continue5test0PUT はただファイルをアップロードするだけなので、デフォルトではContent-Type が指定されません。Content-Type の指定が必要な場合は-H (--header) オプションで追加します。JSON ファイルをアップロードする場合は --json オプションを使い、次で説明する-X オプションで PUT に変えたほうが簡単かもしれません。
$curl-sSfL-H'Content-Type: text/plain'-T file.txt localhost:8080/file.txtPUT /file.txt HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*Content-Type: text/plainContent-Length: 5test$curl-sS-X PUT--json @file.json localhost:8080/file.jsonPUT /file.json HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Content-Type: application/jsonAccept: application/jsonContent-Length: 11{"a": 123}DELETE や PATCH などを使う (-X)
その他の HTTP メソッドに変更したい場合は-X (--request) オプションを使います。
$curl-X DELETE localhost:8080/fileDELETE /file HTTP/1.1Host: localhost:8080User-Agent: curl/8.12.1Accept: */*どの HTTP メソッドに対応しているんだ? と悩む必要はありません。これは単に HTTP メソッドの名前を任意の文字列に変更する機能でしかないので、どんなものにでも変更できます。
- データを送信しない場合は自動的に GET です
- データを送信する場合は
--jsonを使い自動的に POST です - ファイル全体をアップロードするときは
-Tを使い自動的に PUT です -Xオプションで明示的に指定する必要があるのは、これら以外のときだけです
さいごに
はぁーーー、やっと前からアップデートしたかった内容が書けました! HTTP/HTTPS 関連で他になにか便利なcurl の使い方があったら教えてください。
記事公開時は改名前も含めて 1996 年生まれと書いていましたが、公式には 1998 年生まれという扱いのようなので変更しました。↩
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme