<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>json · wleeaf field notes</title><link>https://articles.wleeaf.dev/tags/json/</link><description>Hands-on guides and build logs from the wleeaf studio: self-hosting, Linux, networking, Docker, the command line and reverse engineering.</description><language>en-us</language><lastBuildDate>Fri, 27 Mar 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://articles.wleeaf.dev/tags/json/feed.xml" rel="self" type="application/rss+xml"/><item><title>jq Is the SQL of JSON</title><link>https://articles.wleeaf.dev/posts/jq-command-line-json/</link><pubDate>Fri, 27 Mar 2026 00:00:00 +0000</pubDate><guid>https://articles.wleeaf.dev/posts/jq-command-line-json/</guid><description>Every API returns JSON now. jq turns your terminal into a query engine for it.</description><category>cli</category><category>json</category><category>linux</category><content:encoded><![CDATA[<p>Every API you talk to returns JSON. Every config file is JSON. Every log pipeline emits JSON. And yet most people still pipe <code>curl</code> output through <code>grep</code> and pray.</p>
<p>jq is the tool that makes JSON usable from the command line. Once you learn the basics, you will never go back.</p>
<h2 id="basics">Basics<a class="anchor" href="#basics" aria-label="Link to “Basics”">#</a></h2>
<p>Pretty-print some JSON:</p>
<div class="code">
<div class="code-head"><span class="code-lang">bash</span><button type="button" class="code-copy" data-copy-code hidden><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="2.5"/><path d="M16 8V6.5A2.5 2.5 0 0 0 13.5 4h-7A2.5 2.5 0 0 0 4 6.5v7A2.5 2.5 0 0 0 6.5 16H8"/></svg><span>Copy</span></button></div>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s https://api.github.com/users/torvalds <span class="p">|</span> jq .</span></span></code></pre></div>
</div>
<p>Extract a single field:</p>
<div class="code">
<div class="code-head"><span class="code-lang">bash</span><button type="button" class="code-copy" data-copy-code hidden><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="2.5"/><path d="M16 8V6.5A2.5 2.5 0 0 0 13.5 4h-7A2.5 2.5 0 0 0 4 6.5v7A2.5 2.5 0 0 0 6.5 16H8"/></svg><span>Copy</span></button></div>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;{&#34;name&#34;: &#34;Linus&#34;, &#34;repos&#34;: 5}&#39;</span> <span class="p">|</span> jq .name
</span></span><span class="line"><span class="cl"><span class="c1"># &#34;Linus&#34;</span></span></span></code></pre></div>
</div>
<p>Drop the quotes with <code>-r</code>:</p>
<div class="code">
<div class="code-head"><span class="code-lang">bash</span><button type="button" class="code-copy" data-copy-code hidden><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="2.5"/><path d="M16 8V6.5A2.5 2.5 0 0 0 13.5 4h-7A2.5 2.5 0 0 0 4 6.5v7A2.5 2.5 0 0 0 6.5 16H8"/></svg><span>Copy</span></button></div>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;{&#34;name&#34;: &#34;Linus&#34;}&#39;</span> <span class="p">|</span> jq -r .name
</span></span><span class="line"><span class="cl"><span class="c1"># Linus</span></span></span></code></pre></div>
</div>
<h2 id="arrays">Arrays<a class="anchor" href="#arrays" aria-label="Link to “Arrays”">#</a></h2>
<p>Get the first element:</p>
<div class="code">
<div class="code-head"><span class="code-lang">bash</span><button type="button" class="code-copy" data-copy-code hidden><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="2.5"/><path d="M16 8V6.5A2.5 2.5 0 0 0 13.5 4h-7A2.5 2.5 0 0 0 4 6.5v7A2.5 2.5 0 0 0 6.5 16H8"/></svg><span>Copy</span></button></div>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;[1, 2, 3]&#39;</span> <span class="p">|</span> jq <span class="s1">&#39;.[0]&#39;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 1</span></span></span></code></pre></div>
</div>
<p>Iterate over all elements:</p>
<div class="code">
<div class="code-head"><span class="code-lang">bash</span><button type="button" class="code-copy" data-copy-code hidden><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="2.5"/><path d="M16 8V6.5A2.5 2.5 0 0 0 13.5 4h-7A2.5 2.5 0 0 0 4 6.5v7A2.5 2.5 0 0 0 6.5 16H8"/></svg><span>Copy</span></button></div>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;[{&#34;name&#34;: &#34;a&#34;}, {&#34;name&#34;: &#34;b&#34;}]&#39;</span> <span class="p">|</span> jq <span class="s1">&#39;.[].name&#39;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &#34;a&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># &#34;b&#34;</span></span></span></code></pre></div>
</div>
<h2 id="filtering">Filtering<a class="anchor" href="#filtering" aria-label="Link to “Filtering”">#</a></h2>
<p>This is where it gets powerful. Say you have a list of GitHub repos:</p>
<div class="code">
<div class="code-head"><span class="code-lang">bash</span><button type="button" class="code-copy" data-copy-code hidden><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="2.5"/><path d="M16 8V6.5A2.5 2.5 0 0 0 13.5 4h-7A2.5 2.5 0 0 0 4 6.5v7A2.5 2.5 0 0 0 6.5 16H8"/></svg><span>Copy</span></button></div>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s https://api.github.com/users/torvalds/repos <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  jq <span class="s1">&#39;[.[] | select(.stargazers_count &gt; 1000)] | length&#39;</span></span></span></code></pre></div>
</div>
<p>That filters repos with more than 1000 stars and counts them.</p>
<h2 id="constructing-objects">Constructing Objects<a class="anchor" href="#constructing-objects" aria-label="Link to “Constructing Objects”">#</a></h2>
<p>You can reshape JSON on the fly:</p>
<div class="code">
<div class="code-head"><span class="code-lang">bash</span><button type="button" class="code-copy" data-copy-code hidden><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="2.5"/><path d="M16 8V6.5A2.5 2.5 0 0 0 13.5 4h-7A2.5 2.5 0 0 0 4 6.5v7A2.5 2.5 0 0 0 6.5 16H8"/></svg><span>Copy</span></button></div>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -s https://api.github.com/users/torvalds/repos <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  jq <span class="s1">&#39;.[] | {name: .name, stars: .stargazers_count, lang: .language}&#39;</span></span></span></code></pre></div>
</div>
<p>This outputs a new object for each repo with only the fields you care about.</p>
<h2 id="real-world-parsing-docker">Real World: Parsing Docker<a class="anchor" href="#real-world-parsing-docker" aria-label="Link to “Real World: Parsing Docker”">#</a></h2>
<p>List container names and their memory usage:</p>
<div class="code">
<div class="code-head"><span class="code-lang">bash</span><button type="button" class="code-copy" data-copy-code hidden><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="2.5"/><path d="M16 8V6.5A2.5 2.5 0 0 0 13.5 4h-7A2.5 2.5 0 0 0 4 6.5v7A2.5 2.5 0 0 0 6.5 16H8"/></svg><span>Copy</span></button></div>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker stats --no-stream --format <span class="s1">&#39;{{json .}}&#39;</span> <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  jq -r <span class="s1">&#39;[.Name, .MemUsage] | @tsv&#39;</span></span></span></code></pre></div>
</div>
<p>Find containers using more than 100MB:</p>
<div class="code">
<div class="code-head"><span class="code-lang">bash</span><button type="button" class="code-copy" data-copy-code hidden><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="2.5"/><path d="M16 8V6.5A2.5 2.5 0 0 0 13.5 4h-7A2.5 2.5 0 0 0 4 6.5v7A2.5 2.5 0 0 0 6.5 16H8"/></svg><span>Copy</span></button></div>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker stats --no-stream --format <span class="s1">&#39;{{json .}}&#39;</span> <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  jq -r <span class="s1">&#39;select(.MemPerc | gsub(&#34;%&#34;; &#34;&#34;) | tonumber &gt; 1) | .Name&#39;</span></span></span></code></pre></div>
</div>
<h2 id="working-with-nested-data">Working With Nested Data<a class="anchor" href="#working-with-nested-data" aria-label="Link to “Working With Nested Data”">#</a></h2>
<p>Given deeply nested JSON:</p>
<div class="code">
<div class="code-head"><span class="code-lang">json</span><button type="button" class="code-copy" data-copy-code hidden><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="2.5"/><path d="M16 8V6.5A2.5 2.5 0 0 0 13.5 4h-7A2.5 2.5 0 0 0 4 6.5v7A2.5 2.5 0 0 0 6.5 16H8"/></svg><span>Copy</span></button></div>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;users&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nt">&#34;profile&#34;</span><span class="p">:</span> <span class="p">{</span><span class="nt">&#34;email&#34;</span><span class="p">:</span> <span class="s2">&#34;a@b.com&#34;</span><span class="p">}},</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nt">&#34;profile&#34;</span><span class="p">:</span> <span class="p">{</span><span class="nt">&#34;email&#34;</span><span class="p">:</span> <span class="s2">&#34;c@d.com&#34;</span><span class="p">}}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
</div>
<p>Extract all emails:</p>
<div class="code">
<div class="code-head"><span class="code-lang">bash</span><button type="button" class="code-copy" data-copy-code hidden><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="2.5"/><path d="M16 8V6.5A2.5 2.5 0 0 0 13.5 4h-7A2.5 2.5 0 0 0 4 6.5v7A2.5 2.5 0 0 0 6.5 16H8"/></svg><span>Copy</span></button></div>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">jq <span class="s1">&#39;[.data.users[].profile.email]&#39;</span> data.json
</span></span><span class="line"><span class="cl"><span class="c1"># [&#34;a@b.com&#34;, &#34;c@d.com&#34;]</span></span></span></code></pre></div>
</div>
<h2 id="combining-with-other-tools">Combining With Other Tools<a class="anchor" href="#combining-with-other-tools" aria-label="Link to “Combining With Other Tools”">#</a></h2>
<p>jq plays well with the rest of the Unix toolkit:</p>
<div class="code">
<div class="code-head"><span class="code-lang">bash</span><button type="button" class="code-copy" data-copy-code hidden><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="2.5"/><path d="M16 8V6.5A2.5 2.5 0 0 0 13.5 4h-7A2.5 2.5 0 0 0 4 6.5v7A2.5 2.5 0 0 0 6.5 16H8"/></svg><span>Copy</span></button></div>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Get all unique languages across repos</span>
</span></span><span class="line"><span class="cl">curl -s https://api.github.com/users/torvalds/repos <span class="p">|</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  jq -r <span class="s1">&#39;.[].language // empty&#39;</span> <span class="p">|</span> sort -u
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Convert JSON array to lines for xargs</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;[&#34;file1.txt&#34;, &#34;file2.txt&#34;]&#39;</span> <span class="p">|</span> jq -r <span class="s1">&#39;.[]&#39;</span> <span class="p">|</span> xargs rm
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Merge multiple JSON files</span>
</span></span><span class="line"><span class="cl">jq -s <span class="s1">&#39;add&#39;</span> file1.json file2.json &gt; merged.json</span></span></code></pre></div>
</div>
<h2 id="the-cheat-sheet">The Cheat Sheet<a class="anchor" href="#the-cheat-sheet" aria-label="Link to “The Cheat Sheet”">#</a></h2>
<table>
  <thead>
      <tr>
          <th>Pattern</th>
          <th>What it does</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>.field</code></td>
          <td>Get field</td>
      </tr>
      <tr>
          <td><code>.[]</code></td>
          <td>Iterate array</td>
      </tr>
      <tr>
          <td><code>select(cond)</code></td>
          <td>Filter</td>
      </tr>
      <tr>
          <td><code>map(expr)</code></td>
          <td>Transform array</td>
      </tr>
      <tr>
          <td><code>length</code></td>
          <td>Count</td>
      </tr>
      <tr>
          <td><code>keys</code></td>
          <td>Object keys</td>
      </tr>
      <tr>
          <td><code>to_entries</code></td>
          <td>Object to key-value pairs</td>
      </tr>
      <tr>
          <td><code>@csv</code></td>
          <td>Output as CSV</td>
      </tr>
      <tr>
          <td><code>@tsv</code></td>
          <td>Output as TSV</td>
      </tr>
      <tr>
          <td><code>//</code></td>
          <td>Default value</td>
      </tr>
  </tbody>
</table>
<p>jq has a learning curve, but it is one of those tools where the investment pays off within a week. Every time you find yourself writing a Python script just to parse some JSON, remember that jq probably does it in one line.</p>
]]></content:encoded></item></channel></rss>