diff --git a/notes/shell/bash_rosetta.md b/notes/shell/bash_rosetta.md new file mode 100644 index 0000000..ce0c429 --- /dev/null +++ b/notes/shell/bash_rosetta.md @@ -0,0 +1,86 @@ +# 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 { + 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()) +} +```