Author's note: This question uses outdated syntax and statements from an older version of Nushell. Please see the answer(s) below for the updated syntax.
How do you do while/until loops in Nushell script?
Since Nushell has a fairly amazing table/JSON parsing system, I've been trying to work with the Stack Exchange API through it.
One of the first challenges is looping over the multiple possible pages of results from an API call. My (normally procedural, sometimes OOP) background had me reaching for a construct in Nushell like:
let page = 1
let re = (http (echo "/2.3/questions?fromdate=1648771200&todate=1648944000&order=desc&sort=activity&site=askubuntu&page=" $page) | from json)
let questions = $re.items
while ($re.has_more) {
let page = page + 1
let re = (http (echo "/2.3/questions?fromdate=1648771200&todate=1648944000&order=desc&sort=activity&site=askubuntu&page=" $page) | from json)
let questions = $questions | append $re.items
}
... or the equivalent until construct.
How would I accomplish this in Nushell?
Note - Using httpie in the above example since it automagically handles the gzip compression that the Stack API requires (unlike wget or Nushell's internal fetch command).
Short answer:
As of Nushell 0.72,
whileloops are now supported, along with mutable variables to assist in the handling of such loops.until-style loops can also be replicated throughloop/forwith thebreakstatement.Note: I'm updating this answer to the latest syntax based on the currently available Nushell 0.84. Keep in mind that Nushell continues to evolve its syntax on its path to 1.0.
The example in the question can now be handled fairly easily (as concisely and cleanly as my original question's pseudo-code, at least) with:
This works by:
questionsto collect the results of each iterationforloops over an infinite range of1..http getto retrieve the next page. While in the original question I mentioned that I needed to usehttpieto handle the SE API's gzipped results, the Nushell builtin now handles this properly as well.$"string with ($variable_interprolation)"to update the page number$resholds the payload (the questions) in$res.items), which is then appended to the mutable$questions` list.$res.has_moreto tell us if we need to continue the loop. If not, webreakit. This acts as a simpleuntilcondition.With that in place, you can now access
$questionsto slice-and-dice the results as needed:Result (at the time of the original answer):
And yes, Nushell actually pretty-prints the results table.
Of course,
whileloops are also supported quite cleanly using mutable variables. And a genericloop/breakconstruct isuntilshort-hand forfor x in 1..when you don't need an incrementing variable.From the Nushell help:
While:
mut x = 0; while $x < 10 { $x = $x + 1 }Loop:
mut x = 0; loop { if $x > 10 { break }; $x = $x + 1 }; $xAlthough in reality that would be better served with a
for, of course.Older info:
However, my original, pre-0.72 answer, using recursive functions, is still a valid way of handling this (and may a useful technique), but do keep in mind that Nushell does not have tail-recursion.
Using recursion, a basic "while" loop in Nushell might look something like:
And a corresponding until-loop might look like:
If you need to modify a variable, keep in mind that it is scoped to its block, so you'll need to pass it back in to the recursive function. For instance, to work with the Stack Exchange API and update the page number for each call:
Note that each future call is
appended to the current results.Also note that the return results must be the last statement executed in the function.
Side-note: Personal opinion -- I envision that Nushell will eventually add a
yieldkeyword to allow generator expressions. This will simply the above example further by allowing it inside areducethat can accumulate the results.