{
  "version": "https://jsonfeed.org/version/1",
  "title": "Ian's Digital Garden",
  "home_page_url": "https://ianwwagner.com/",
  "feed_url": "https://ianwwagner.com//tag-nushell.json",
  "description": "",
  "items": [
    {
      "id": "https://ianwwagner.com//better-shell-scrollback-with-timestamps.html",
      "url": "https://ianwwagner.com//better-shell-scrollback-with-timestamps.html",
      "title": "Better Shell Scrollback with Timestamps",
      "content_html": "<p>Last week I noticed a pattern in some of my shell workloads:\nrun something in a shell, go change some code, run it again.\nAnd often I'm looking for some sort of change between runs.</p>\n<p>One of the most common things that could change (which I may not always notice)\nis how long the thing took to run.\nYou could solve this with a utility like <code>time</code>, but you have to remember that,\nand it also pollutes your shell history, obscuring the actual thing you were trying to run.</p>\n<p>Another common workflow is comparing somewhat detailed log outputs for differences.\nA single shell scrollback buffer is TERRIBLE for this,\nbecause you can't compare things side-by-side.\nSo I often use multiple tabs/buffers/windows.\nThis introduces a new problem though: remembering which one you ran last!\nIt sounds silly, but I may go heads down for hours and it's very easy to forget!</p>\n<p>I solved both of these with a better shell configuration.\nI'm using <a href=\"https://www.nushell.sh/\">nushell</a>, but something similar should be achievable in most shells:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-nu\"># Right prompt: duration of the last command.\n$env.PROMPT_COMMAND_RIGHT = {||\n    let ms = ($env.CMD_DURATION_MS? | default &quot;0&quot; | into int)\n    if $ms &lt; 1 {\n        &quot;&quot;\n    } else {\n        $&quot;took (($ms * 1_000_000) | into duration)&quot;\n    }\n}\n$env.TRANSIENT_PROMPT_COMMAND_RIGHT = null\n\n# Inserts a timestamp right before executing a command (after you press enter).\n# I tried doing this with a transient prompt, but one interesting (useful?)\n# property of nushell is that many things are required to be known and static at some point in time.\n# This property applies to prompts.\n# The prompt is evaluated only once, so the timestamp is the moment at which the prompt was rendered,\n# which may be long before you execute the next command.\n# So barring weird terminal tricks to edit previous lines, this is the best solution I've come up with so far.\n$env.config = (\n    $env.config\n    | upsert hooks.pre_execution [ {||\n        # $env.repl_commandline = (commandline)\n        # print $&quot;Command: ($env.repl_commandline)&quot;\n        let stamp = (date now | format date &quot;%Y-%m-%dT%H:%M:%S%:z&quot;)\n        print $&quot;@($stamp)&quot;\n    } ]\n)\n</code></pre>\n<p>Once you've made the edits, you can reload your config with <code>exec nu</code>.\nAnd now your scrollback is a log!\nEvery entry has a start timestamp and a duration.</p>\n",
      "summary": "",
      "date_published": "2026-05-11T00:00:00-00:00",
      "image": "",
      "authors": [
        {
          "name": "Ian Wagner",
          "url": "https://fosstodon.org/@ianthetechie",
          "avatar": "media/avi.jpeg"
        }
      ],
      "tags": [
        "shell",
        "nushell"
      ],
      "language": "en"
    },
    {
      "id": "https://ianwwagner.com//delightfully-simple-pipelines-with-nushell.html",
      "url": "https://ianwwagner.com//delightfully-simple-pipelines-with-nushell.html",
      "title": "Delightfully Simple Pipelines with Nushell",
      "content_html": "<p>I've been using <a href=\"https://www.nushell.sh/\">nushell</a> as my daily driver for about six months now,\nand wanted to show a few simple examples of why I'm enjoying it so much.\nI think it's a breath of fresh air compared to most shells.</p>\n<h1><a href=\"#why-a-new-shell\" aria-hidden=\"true\" class=\"anchor\" id=\"why-a-new-shell\"></a>Why a new Shell?</h1>\n<p>In case you've never heard of it before, nushell is a, well, new shell ;)\n<code>bash</code> has been the dominant shell for as long as I can remember,\nthough <code>zsh</code> have their fair share of devotees.\n<code>fish</code> is the only recent example I can think of as a &quot;challenger&quot; shell.\n<code>fish</code> gained enough traction that it's supported by tooling such as Python <code>virtualenv</code>\n(which only has integrations out of the box for a handful of shells).\nI think <code>fish</code> is popular because it had some slightly saner defaults out of the box,\nwas easier to &quot;customize&quot; with flashy prompts (which can make your shell SUPER slow to init),\nand had a saner scripting language than <code>bash</code>.\nBut it still retained a lot of the historical baggage from POSIX shells.</p>\n<p>Nushell challenges two common assumptions about shells\nand asks &quot;what if things were different?&quot;</p>\n<ol>\n<li>POSIX compliance is a non-goal.</li>\n<li>Many standard tools from GNU coreutils/base system (e.g. <code>ls</code> and <code>du</code>) are replaced by builtins.</li>\n<li>All nushell &quot;native&quot; utilities produce and consume <strong>structured data</strong> rather than text by default.</li>\n</ol>\n<p>By dropping the goal of POSIX compliance,\nnushell frees itself from decades of baggage.\nThis means you get a scripting language that feels a lot more like Rust.\nYou'll actually get errors by default when you try to do something stupid,\nunlike most shells which will happily proceed,\nusually doing something even more stupid.\nMaybe treating undefined variables as empty string make sense in the 1970s,\nbut that's almost never helpful.</p>\n<p>nushell also takes a relatively unique approach to utilities.\nWhen you type something like <code>ls</code> or <code>ps</code> in nushell,\nthis is handled by a shell builtin!\nIt's just Rust code baked into the shell rather than calling out to GNU coreutils\nor whatever your base system includes.\nThis means that whether you type <code>ps</code> on FreeBSD, Debian, or macOS,\nyou'll get the same behavior!</p>\n<p>I can already hear some readers thinking &quot;doesn't this just massively bloat the shell?&quot;\nNo, not really.\nThe code for these is for less than that of the typical GNU utility,\nbecause nushell actually (IMO) embraces UNIX philosophy even better than the original utilities.\nThey are all extremely minimal and work with other builtins.\nFor example, there are no sorting flags for <code>ls</code>,\nand no format/unit flags for <code>du</code>.</p>\n<p>The reason that nushell <em>can</em> take this approach is because they challenge the notion that\n&quot;text is the universal API.&quot;\nYou <em>can't</em> meaningfully manipulate text without lots of lossy heuristics.\nBut you <em>can</em> do this for structured data!\nI admit I'm a bit of a chaos monkey, so I love to see a project taking a rare new approach\nin space where nothing has fundamentally changed since the 1970s.</p>\n<p>Okay, enough about philosophy... here are a few examples of some shell pipelines I found delightful.</p>\n<h1><a href=\"#elastic-snapshot-status\" aria-hidden=\"true\" class=\"anchor\" id=\"elastic-snapshot-status\"></a>Elastic snapshot status</h1>\n<p>First up: I do a lot of work with Elasticsearch during <code>$DAYJOB</code>.\nOne workflow I have to do fairly often is spin up a cluster and restore from a snapshot.\nThe Elasticsearch API is great... for programs.\nBut I have a hard time grokking hundreds of lines of JSON.\nHere's an example of a pipeline I built which culls the JSON response down to just the section I care about.</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-nu\">http get &quot;http://localhost:9200/myindex/_recovery&quot;\n  | get restored-indexname\n  | get shards\n  | get index\n  | get size\n</code></pre>\n<p>In case it's not obvious, the <code>http</code> command makes HTTP requests.\nThis is another nushell builtin that is an excellent alternative to <code>curl</code>.\nIt's not as feature-rich (<code>curl</code> has a few decades of head start),\nbut it brings something new to the table: it understands from the response that the content is JSON,\nand converts it into structured data!\nEverything in this pipeline is nushell builtins.\nAnd it'll work on <em>any</em> OS that nushell supports.\nEven Windows!\nThat's wild!</p>\n<p>Pro tip: you can press option+enter to add a new line when typing in the shell.</p>\n<h1><a href=\"#disk-usage-in-bytes\" aria-hidden=\"true\" class=\"anchor\" id=\"disk-usage-in-bytes\"></a>Disk usage in bytes</h1>\n<p>Here's an example I hinted at earlier.\nIf you type <code>help du</code> (to get built-in docs),\nyou won't find any flags for changing the units.\nBut you can do it using formatters like so:</p>\n<pre class=\"marmite-code\"><code class=\"marmite-code-inner language-nu\">du path/to/bigfile.bin | format filesize B apparent\n</code></pre>\n<p>The <code>du</code> command <em>always</em> shows human-readable units by default. Which I very much appreciate!\nAnd did you notice <code>apparent</code> at the end there?\nWell, the version of <code>du</code> you'd find with a typical Linux distro doesn't <em>exactly</em> lie to you,\nbut it withholds some very important information.\nThe physical size occupied on disk is not necessarily the same as how large the file\n(in an abstract platonic sense) <em>actually</em> is.</p>\n<p>There are a bunch of reasons for this, but the most impactful one is compressed filesystems.\nIf I ask Linux <code>du</code> how large a file is in an OpenZFS dataset,\nit will report the physical size by default, which may be a few hundred megabytes\nwhen the file is really multiple gigabytes.\nNot <em>necessarily</em> helpful.</p>\n<p>Anyways, the nushell builtin always gives you columns for both physical and apparent.\nSo you can't ignore the fact that these sizes are often different.\nI like that!</p>\n<h1><a href=\"#some-other-helpful-bits-for-switching\" aria-hidden=\"true\" class=\"anchor\" id=\"some-other-helpful-bits-for-switching\"></a>Some other helpful bits for switching</h1>\n<p>If you want to give nushell a try,\nthey have some great documentation.\nRead the basics, but also check out their specific pages on, e.g.\n<a href=\"https://www.nushell.sh/book/coming_from_bash.html\">coming from bash</a>.</p>\n<p>Finally, here are two more that tripped me up at first.</p>\n<ul>\n<li>If you want to get the PID of your process, use <code>$nu.pid</code> instead of <code>$$</code>.</li>\n<li>To access environment <em>variables</em>, you need to be explicit and go through <code>$env</code>. On the plus side, you can now explicitly differentiate from environment variables.</li>\n</ul>\n",
      "summary": "",
      "date_published": "2025-12-13T00:00:00-00:00",
      "image": "",
      "authors": [
        {
          "name": "Ian Wagner",
          "url": "https://fosstodon.org/@ianthetechie",
          "avatar": "media/avi.jpeg"
        }
      ],
      "tags": [
        "shell",
        "nushell"
      ],
      "language": "en"
    }
  ]
}