87 lines
2.5 KiB
Markdown
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())
|
|
}
|
|
```
|