[Short Tip] Get all columns in a table

When working with larger data structures in Nushell, there are often tables that are wider than the terminal has width, resulting in some columns truncated, indicated by the three dots .... But how can we expand the dots?

❯ ls -la
╭───┬──────────────────┬──────┬────────┬──────────┬─────╮
│ # │ name │ type │ target │ readonly │ ... │
├───┼──────────────────┼──────┼────────┼──────────┼─────┤
│ 0 │ 213-3123-43432.p │ file │ │ false │ ... │
│ │ df │ │ │ │ │
│ 1 │ barcode-picture. │ file │ │ false │ ... │
│ │ jpg │ │ │ │ │
│ 2 │ print-me-by-tomo │ file │ │ false │ ... │
│ │ rrow.pdf │ │ │ │ │
╰───┴──────────────────┴──────┴────────┴──────────┴─────╯

The answer is simple, but surprisingly, not easily found. The “Working with tables” documentation of Nushell weirdly doesn’t tell, for example. The trick is to use the command columns to get a list of all column names:

❯ ls -la|columns
╭────┬───────────╮
│ 0 │ name │
│ 1 │ type │
│ 2 │ target │
│ 3 │ readonly │
│ 4 │ mode │
│ 5 │ num_links │
│ 6 │ inode │
│ 7 │ user │
│ 8 │ group │
│ 9 │ size │
│ 10 │ created │
│ 11 │ accessed │
│ 12 │ modified │
╰────┴───────────╯

And once you know that command, you can easily find the corresponding Nushell documentation: nushell.sh/commands/docs/columns.html

[Short Tip] Processing line by line in a loop in Nushell

For a test I recently had to process a plain list of items that was outputted by a program. In Bash, the usual way to do so is:

while read -r line; do COMMAND $line; done

But how could this be done in Nushell? Just using the same command gives an error:

❯ flatpak list|grep system|cut -f 2|while read -r line; do flatpak info $line; done
Error: nu::parser::parse_mismatch

× Parse mismatch during operation.
╭─[entry #2:1:1]
1 │ flatpak list|grep system|cut -f 2|while read -r line; do flatpak info $line; done
· ─┬
· ╰── expected operator
╰────

❯ flatpak list|grep system|cut -f 2|while read line; do flatpak info $line; done
Error: nu::parser::parse_mismatch

× Parse mismatch during operation.
╭─[entry #3:1:1]
1 │ flatpak list|grep system|cut -f 2|while read line; do flatpak info $line; done
· ──┬─
· ╰── expected block, closure or record
╰────

Instead, the trick is to tell Nushell to read the input line by line with lines, and then process each and every item with a sub-function:

flatpak list|grep system|cut -f 2| lines|each { |it| flatpak info ($it) }

This worked flawlessly.

[Short Tip] Using a Python virtual environment in Nushell

Nushell is becoming a more and more serious shell every day. One thing missing in the past was the capability to create and use Python virtual environments.

This has changed: Nushell was added as another supported shell in the virtualenv package:

🕙(20:39:55) ~/development
❯ virtualenv ansible
created virtual environment CPython3.11.5.final.0-64 in 190ms
creator CPython3Posix(dest=/home/liquidat/development/ansible, clear=False, no_vcs_ignore=False, global=False)
seeder FromAppData(extra_search_dir=/usr/share/python-wheels,download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/home/liquidat/.local/share/virtualenv)
added seed packages: pip==22.3.1, setuptools==65.5.1, wheel==0.38.4
activators BashActivator,CShellActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator

However, there is one catch: the source command does not work when you try to use it to switch to the new environment:

🕙(20:40:28) ~ 
❯ source ~/development/ansible/bin/activate.nu
Error: nu::parser::unexpected_keyword

  × Statement used in pipeline.
     ╭─[/home/liquidat/development/ansible/bin/activate.nu:116:1]
 116 │ export alias pydoc = python -m pydoc
 117 │ export alias deactivate = overlay hide activate
     ·                           ───┬───
     ·                              ╰── not allowed in pipeline
     ╰────
  help: 'overlay' keyword is not allowed in pipeline. Use 'overlay' by itself, outside of a pipeline.

Instead, you need to use the overlay command:

🕙(20:40:50) ~ 
❯ overlay use ~/development/ansible/bin/activate.nu
(ansible)

Afterwards, you can continue to operate in the environment like usual:

🕙(20:42:41) ~/development/ansible via 🐍 v3.11.5 (ansible) 
❯ pip install ansible
Collecting ansible
  Downloading ansible-8.4.0-py3-none-any.whl (47.4 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 47.4/47.4 MB 19.6 MB/s eta 0:00:00
[...]

[notice] A new release of pip available: 22.3.1 -> 23.2.1
[notice] To update, run: pip install --upgrade pip
(ansible) 
🕙(20:43:15) ~/development/ansible via 🐍 v3.11.5 (ansible) took 20s 
❯ 

[Short Tip] Plot live-data in Linux terminal

Recently I realized that one of the disks in my server had died. After the replacement, the RAID sync started – and I quickly had to learn that this was going to take days (!). But I also learned that the time it might take massively jumped up and down.

Thus I thought it would be fun to monitor the progress of this. First, I just crated a command to watch the minutes (calculated into days) every few seconds with watch:

watch 'cat /proc/mdstat |grep recovery|cut -d " " -f 13|cut -d "=" -f 2|cut -d "." -f 1|xargs -n 1 -I {} echo "{}/60/24"|bc'

But since it was jumping so much I was wondering if I could live-plot the data in the terminal (remote server, after all). There are many ways to do that, even gnuplot seems to have an options for that, but I wanted something more simple. Enter: pipeplot

First I tried to use watch together with pipeplot, but it was easier to just write a short for loop around it:

while true;
do
  cat /proc/mdstat |grep recovery|cut -d " " -f 13|cut -d "=" -f 2|cut -d "." -f 1|xargs -n 1 -I {} echo "{}/60/24"|bc;
  sleep 5;
done \
| pipeplot

And the result is rather nice (also shown in the header image):

[Short Tip] Accessing tabular nushell output for non-nushell commands

After I learned how subshells can be executed within nushell I was confident that I could handle that part. But few minutes ago I run into an error I didn’t really understand:

❯ rpm -qf (which dwebp)
error: Type Error
   ┌─ shell:24:16
   │
24 │ rpm -qf (which dwebp)
   │                ^^^^^ Expected string, found row

I thought the parameter was provided somehow in the wrong way, and put it into quotes: "dwebp". But it didn’t help. I tested around more with sub-shells, some of them worked while others didn’t. The error message was misleading for me, letting me think that there is a difference in how the argument is interpreted:

❯ rpm -qi (echo rpm)
Name        : rpm
Version     : 4.16.1.3
Release     : 1.fc34
[...]

❯ echo (which dwebp)
───┬───────┬────────────────┬─────────
 # │  arg  │      path      │ builtin 
───┼───────┼────────────────┼─────────
 0 │ dwebp │ /usr/bin/dwebp │ false   
───┴───────┴────────────────┴─────────

It took me a while until I understood what I was looking at – and to make the error message make sense: the builtin nushell command which can give back multiple results, thus returning a table. The builting nushell command echo returns a string!

Thus the right way to execute my query is to get the content of the cell of the table I am looking at via get:

❯ rpm -qf (which dwebp|get path|nth 0)
libwebp-tools-1.2.1-1.fc34.x86_64

Note that nth 0 is not strictly necessary here since there is only one item in the table anyway. But it might help as a reference for future examples.

You don’t have to use pipe, btw., there is an even shorter way available:

❯ rpm -qf (which dwebp|nth 0).path
libwebp-tools-1.2.1-1.fc34.x86_64