Compare commits

...

3 Commits

  1. 146
      content/code/command-test.md
  2. 101
      content/presentations/algo-ganho-algo-perdido/index.pt.md

146
content/code/command-test.md

@ -0,0 +1,146 @@
+++
title = "Running a Command and Saving Its Output to File in Rust"
date = 2023-09-01
[taxonomies]
tags = ["random", "rust", "command", "log"]
+++
I had an issue: I needed to run a command inside Rust, but I needed that all
its output should go to a file, and I needed to check if there were certain
phrases in it.
<!-- more -->
So, first step: Create a script that could "replicate" the output of a command,
with the expected strings to be captured:
```bash
#!/usr/bin/env bash
for loop in {1..1000}
do
echo "Hello, I'm a script!"
echo "I write stuff in the output."
echo "Everything should go to a file."
echo "But also, you need to capture warnings:"
if (( $loop%7 == 0)); then
echo "WARNING: This is a warning"
echo " It continues if the line starts with spaces"
echo " And keeps going till there are no more spaces-prefixes"
fi
if (( $loop%8 == 0)); then
# ERR is just to make sure we find it easily in the logs
echo "ERR: Sometimes, I also write in stderr!" >&2
echo "ERR: Just for funsies!" >&2
fi
echo "Like this."
echo "Then you're good to go."
echo ""
done
```
What this script does is to print a message over 1,000 times, and sometimes it
will display a "WARNING" text -- which is the special output I need to capture --
and sometimes it will print things to stderr.
For the code, what we need to do is:
1. Spawn the command;
2. Take the stdour (and stderr) from it.
3. Spawn a thread that will keep listening to the output, doing the search,
and writing everything to a file (our log).
4. The thread returns the list of captured messages, which we can get back
when we `.join()` it.
5. Since I was expecting stderr to be smaller enough, I did the capturing of
it after the thread completes (which would also close the file, so we can
be sure that we can open it again without any issues).
The first step is quite easy: Just use `std::process::Command` and use the
`.spawn()` function to create the `Child` controller.
For the second step, we use the `Child` structure and use `.take()` on both
stdout and stderr. This will give us the file descriptor for both (think about
them as `File`s).
The third step is quite easy, actualy: `std::thread::spawn()` to create a
thread, and just read the content from the file descriptors from step 2. In
this, I used `BufReader`, which gives access to reading the content line by
line, which is way easier than reading to a buffer and processing it.
```rust
use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader, Read, Write};
use std::path::PathBuf;
use std::process::Command;
fn main() {
// this requires always running with `cargo run`
let base = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let the_script = base.join("src").join("the_script.sh");
let mut cmd = Command::new("bash")
.arg(the_script)
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
.unwrap();
// capture both the stdout and stderr as File structs (actually FDs, but basically the same
// thing)
let stdout = cmd.stdout.take().unwrap();
let mut stderr = cmd.stderr.take().unwrap();
// spawn a thread to keep capturing and processing the stdout.
let writer_pid = std::thread::spawn(move || {
let reader = BufReader::new(stdout);
let lines = reader.lines();
let mut log_file = File::create("script.log").unwrap();
let mut in_warning = false;
let mut result = Vec::new();
for line in lines {
let line = line.unwrap();
log_file.write(line.as_bytes()).unwrap();
log_file.write(b"\n").unwrap(); // 'cause lines() eat it
if line.starts_with("WARNING:") {
in_warning = true;
} else if line.starts_with(" ") && in_warning {
result.push(line);
} else if in_warning {
in_warning = false;
}
}
result
});
// run the command till it finishes
cmd.wait().unwrap();
// ... and wait till the thread finishes processing the whole output.
let warnings = writer_pid.join().unwrap();
// this is somewhat a hack: Instead of spawning a thread for stderr and trying to fight with
// stdout for the lock to be able to write in the log file, we do this after the thread ends
// (which closes the file) and then open it again and write the stderr in the end. We do this
// 'cause we expect that the stderr is way smaller than stdout and can fit in memory without
// any issues.
let mut buffer = String::new();
stderr.read_to_string(&mut buffer).unwrap();
let mut file = OpenOptions::new().append(true).open("script.log").unwrap();
file.write(buffer.as_bytes()).unwrap();
// This is purely for diagnostic purposes. We could put the warnings in another file, or pass
// it along to something else to process it. Here, we just display them.
// Same for stderr: Since we already put them in the file, this is used just to make sure we
// are capturing the errors without looking at the file.
println!("Warnings:\n{:?}", warnings);
println!("ERR:\n{:?}", buffer)
}
```

101
content/presentations/algo-ganho-algo-perdido/index.pt.md

@ -0,0 +1,101 @@
+++
title = "Algo Ganho, Algo Perdido"
date = 2023-10-20
draft = true
[taxonomies]
tags = ["apresentações", "pythonbrasil"]
+++
<!-- more -->
Minha vida com comunidades de programação começou em 1997, quando foi
bolsista de iniciação científica na Unisinos. Na época, a internet
ainda estava engatinhando no Brasil, e no grupo de bolsistas a ideia
de software livre estava se tornando cada vez mais forte. Linux era a
nossa principal ferramenta, as aplicações ainda eram bem crus e, para
um grupo de jovens na casa dos 20 anos, viciados em programar e com
algum tempo livre, mexer nas coisas dos outros era o mais legal -- e
ainda mais legal era ter acesso ao código dos outros, e poder ver como
outras pessoas resolviam seus problemas, e poder alterar o código para
fazer algo que queríamos, era o máximo.
E eu posso dizer que eu fui muito sortudo em estar no meio desse
grupo.
Mas faculdade termina, e bolsas de estudo terminam ainda mais rápido,
e logo eu acabei me afastando disso.
... até 2016, quando participei da minha primeira PythonBrasil.
Num dos corredores, entre os períodos de apresentações, eu estava
conversando com algumas pessoas quando alguém -- cujo nome eu nunca
perguntei -- veio dizendo que éramos "gaúchos de araque", porque não
tínhamos ido tentar defender Porto Alegre como próxima sede do evento.
Eu tive que ser bruto: "Nós não conseguimos nem manter um evento
local, e agora querem que a gente faça um evento nacional?" Por sorte,
o Filipe Cifali, organizador do PyTche, estava do meu lado, e comentou
que ele não estava conseguindo manter tudo sozinho. Naquele momento,
eu me ofereci pra tentar erguer o grupo, organizando as coisas junto
com o Filipe. E, no fim, Tony Dourado se juntou a nós, e o grupo teve
3 organizadores.
E por 3 anos, o PyTche continuou tendo eventos todos os meses, talvez
com a ocasional exclusão de Dezembro e Janeiro, por causa do Natal e
primeiro mês do ano, quando boa parte do pessoal estava ainda de
férias, ou nos meses em que havia PythonBrasil e os três organizadores
estavam afastados.
Na época, o "queridinho" para organizar comunidades era o Meetup. E, a
partir dele nós tínhamos fotos dos eventos, organização de enquetes
(que incluía "Qual o melhor dia da semana de X para fazer o
encontro?", em que, invariavelmente, sábado de manhã ganhava), e
organização da lista de presença (para encontrar o lugar do evento).
Infelizmente, depois de 2019, o PyTche não teve mais encontros.
Em 2018, fui contatado pelo Leonardo Vaz para participar do Tchelinux.
Ao contrário do PyTche (e outras comunidades) que se organizavam
localmente, a ideia do Tchelinux era ir aos lugares, para falar com
quem estava começando a estudar programação e mostrar que haviam
ferramentas que eles poderiam não só ter de graça, mas também poderiam
aprender com elas, e modificar da forma que quisessem. Todo o processo
era organizado pelo Leonardo, que encontrava professores da área de
ciências de computação de alguma universidade e perguntava se eles
tinham interesse em trabalhar com o Tchelinux para fazer um evento na
universidade.
Em 2019 eu participei de 13 dos 15 eventos do Tchelinux, percorrendo
quase 3.000km pelo estado do Rio Grande do Sul para fazer minhas
apresentações -- entre elas, "Fugindo Para as Colinas com Python", em
que eu desconstruo uma aplicação em Python, explicando o que cada
parte faz e por que algumas decisões de design foram tomadas. Mas a
parte mais legal era que, quando me perguntavam como continuar
aprendendo Python, eu comentava da experiência com o PyTche, dos
encontros e como cada um trazia alguma coisa, ou tentávamos resolver,
em conjunto, um problema do Exercism, e sempre havia uma animação em
fazer algo parecido.
Mas 2019 foi o meu último ano com o Tchelinux.
Por volta do começo de 2018 eu comecei a me interessar pela linguagem
Rust, e descobri que havia um grupo local, chamado "Rust in POA",
através do Meetup. Como o grupo estava meio parado por falta de tempo
dos organizadores, e como eu já tinha experiência em organizar esse
tipo de evento, me comprometi em ajudar na organização.
No começo de 2020, o Rust in POA parou de se encontrar.
O motivo dessas paradas é óbvio: Em 2020, tivemos os primeiros efeitos
da COVID-19 no Brasil, e ninguém sabia como o vírus se espalhava ou
como se proteger, e contatos pessoais ou próximos foram veemente
desencorajados. Nesse cenário, a utilização de outras ferramentas de
comunicação contínua, como Telegram e WhatsApp explodiram, e a
utilização de ferramentas de vídeo chamada também.
<!--
vim:spelllang=pt:spell:tw=70
-->
Loading…
Cancel
Save