2 minutes
Written: 2024-07-22 00:09 +0000
xonsh Excursions
Background
Recently I found myself writing a bunch of search and replace one-liners.
1export FROM='Matrix3d'; export TO='Matrix3S'; ag -l $FROM | xargs -I {} sd $FROM $TO {}
Which works, especially since both ag and sd are rather good, but it is still:
- Slightly non-ergonomic to type
- Difficult to keep track of
- Modulo dumping everything in a
.shfile
- Modulo dumping everything in a
These reminded me of the rich set of alternate shells1.
Reaching for xonsh
Although nushell, elvish and even oil seemed promising, I settled on the
Python based xonsh.
1pipx install xonsh[full]
2pipx inject xonsh xcontrib-sh
3echo 'xontrib load sh' >> ~/.xonshrc
Where we use the injection mechanism to add the xcontrib-sh plugin for running
shell commands without using xonsh, since we might not want to translate every
command to be compliant with xonsh.
Additions
xonsh-modeworks well withemacsand is on MELPA- xxh makes it pretty trivial to take into foreign SSH machines
Scripting substitutions
Directly leveraging xonsh we can rewrite the earlier bash script into:
1from pathlib import Path
2
3def replace_make_shared():
4 FROM_MAKE = 'Matter'
5 TO_MAKE = 'Matter'
6
7 files = $(ag -l @(FROM_MAKE)).splitlines()
8
9 for f in files:
10 if Path(f).exists():
11 sd @(FROM_MAKE) @(TO_MAKE) @(f)
12 else:
13 echo "File not found: @(file)"
14
15replace_make_shared()
Leveraging Python
It is instructive to recall how this would work in pure python.
1import subprocess
2from pathlib import Path
3
4def replace_make_shared():
5 FROM_MAKE = 'std::shared_ptr<Matter>'
6 TO_MAKE = 'Matter*'
7
8 result = subprocess.run(['ag', '-l', FROM_MAKE],
9 capture_output=True, text=True)
10 files = result.stdout.splitlines()
11 actionable_files = [x for x in files if 'migrator' not in x]
12
13 for f in actionable_files:
14 file_path = Path(f)
15 if file_path.exists():
16 subprocess.run(['sd', FROM_MAKE, TO_MAKE,
17 str(file_path)], check=True)
18 else:
19 print(f"File not found: {file_path}")
20
21replace_make_shared()
With sh, shell calls become more ergonomic, however, it is still easier to
interoperate between the shell outputs and Python code (e.g. using echo) using
xonsh.
Conclusions
Personally, xonsh hits the sweet-spot of being slightly less finicky than
POSIX shells while being less verbose than the pure python variant. It isn’t
perfect though:
- The
pythondependence, even withpipxcan be an issue 2- Packages which are needed have to be injected into the same environment..
nixmaybe…
- Packages which are needed have to be injected into the same environment..
- There is no good testing framework
- Ugly
subprocessbasedpytesttests could be written
- Ugly
For now, though, this is sufficiently advantageous.
True of all python packages ↩︎
