dotfiles/notes/shell/bash_rosetta.md

87 lines
2.5 KiB
Markdown

# Using shell in other languages
Bash is incredibly concise at allowing programs to interact on a basic level,
however it severely lacks control flow and sensible data structures, which make
it a bad choice for logic-heavy scripting
In these cases we can attempt to recreate the high-level control bash has over
programs in other languages. All languages are able to call programs exactly
like bash, though almost all of them are much more verbose
The examples below use translate line of bash
```bash
swaymsg -t get_tree | jq '.nodes[1].nodes[].representation' | tr -d '\"'
```
## Python
A great and portable choice for easy scripting
```python
from subprocess import Popen, PIPE
p1 = Popen(["swaymsg", "-t", "get_tree"], stdout=PIPE);
p2 = Popen(["jq", ".nodes[1].nodes[].representation"],
stdin=PIPE,
stdout=PIPE);
p3 = Popen(["tr", "-d", '"'), stdin=PIPE, stdout=PIPE)
p1_stdout, _ = p1.communicate(timeout=1)
p2_stdout, _ = p2.communicate(p1_stdout, timeout=1)
p3_stdout, _ = p3.communicate(p2_stdout, timeout=1)
print(str(p3_stdout))
```
Shell-like strings can easily be split into the args lists required by Popen
```python
from shlex import split as shsplit
# ...
p3 = Popen(shsplit("tr -d '\"'"), stdin=PIPE, stdout=PIPE)
# ...
```
Do not use pipes even in the shsplit strings. Pipes must be manually managed,
unless the `shell=True` parameter is passed to Popen. However, that essentially
completely gives up control over the subprocess, so it's highly not recommend
## Rust
Rust is very fast, though not very portable. Portable binaries can be made by
compiling with a musl libc target. That way everything will be statically linked
into the binary, which typically makes it work across all Linux systems
```bash
cargo build --release --target=x86_64-unknown-linux-musl
```
Rust is extremely verbose, though allows for very fine error handling. The below
uses unwrap mostly
```rust
use std::io::{self, BufRead, BufReader};
use std::process::{Command, Stdio, ChildStdout};
fn get_tree() -> io::Result<ChildStdout> {
let swaymsg = Command::new("swaymsg")
.arg("-t")
.arg("get_tree")
.stdout(Stdio::piped())
.spawn()?;
let jq = Command::new("jq")
.arg(".nodes[1].nodes[].representation")
.stdin(swaymsg.stdout.unwrap())
.stdout(Stdio::piped())
.spawn()
.expect("`jq` binary is not available");
let tr = Command::new("tr")
.arg("-d")
.arg("\"")
.stdin(jq.stdout.unwrap())
.stdout(Stdio::piped())
.spawn()?;
Ok(tr.stdout.unwrap())
}
```