Código de saída, erro e Ctrl-C
O que o shell lê, o que vai para o stderr, e como terminar sem deixar sujeira.
Um programa de terminal conversa com o shell por três canais, e dois deles são invisíveis para quem só olha a tela: o código de saída, o stderr e os sinais.
O código de saída é a resposta que o script lê#
| Código | Convenção |
|---|---|
0 | deu certo — e só isso |
1 | falhou por um motivo do programa |
2 | uso errado: argumento faltando, flag desconhecida |
130 | interrompido com Ctrl-C (128 + SIGINT) |
dataforge
adopt Arcane.OS as OS
action principal(args):
given args["entrada"] is void:
// Uso errado sai com 2, e a mensagem vai para o STDERR:
// quem canaliza a saída quer o dado, não o erro.
yield 2
yield 0
assert principal({"entrada": void}) is 2
assert principal({"entrada": "a.csv"}) is 0O erro vai para o stderr porque prog > saida.txt guarda só o resultado: misturar os dois faz a mensagem de erro entrar no arquivo de dados, e ninguém nota até o dia em que o arquivo é lido por outro programa.
dataforge
adopt Arcane.OS as OS
// O dado vai para a saída normal…
out "linha de dado"
// …e a queixa, para o stderr. Aqui, à mão, para o bloco não encerrar:
// 'Cli.erro(texto)' faz as duas coisas — escreve no stderr E SAI com 1.
// É o 'die' de um script, e por isso não há linha depois dele.
assert OS.is_tty() is yes or OS.is_tty() is noencerra com 1
adopt Arcane.Cli as Cli
// Última linha do programa, de propósito: ela encerra com código 1.
Cli.erro("não achei o arquivo 'vendas.csv'")Terminar sem deixar sujeira#
defer roda na saída da ação onde quer que esteja escrito — inclusive no topo do programa, no fim dele. É o que garante que o arquivo temporário some, a conexão fecha e o relé desliga:
dataforge
adopt Arcane.IO as IO
adopt Arcane.OS as OS
pasta := $"{OS.temp_dir()}/df-cli-{randint(100000, 999999)}"
IO.mkdir(pasta)
defer:
IO.remove_tree(pasta)
IO.write($"{pasta}/trabalho.txt", "processando")
assert IO.exists($"{pasta}/trabalho.txt")
out "o defer apaga a pasta ao sair — inclusive se o programa falhar"O `Ctrl-C`#
dataforge
adopt Arcane.Inicio as Inicio
// 'ao_encerrar' roda no caminho normal E no Ctrl-C: é onde mora
// "salve o que deu tempo" e "solte o que estiver segurando".
salvos := []
Inicio.ao_encerrar(lambda => salvos.append("estado gravado"))
out "encerramento registrado"Erro que a pessoa consegue resolver#
dataforge
adopt Arcane.IO as IO
action abrir(caminho):
given not IO.exists(caminho):
// O que, onde, e o que fazer. As três partes.
trigger $"não achei '{caminho}'. Confira o caminho, ou passe '-' para ler da entrada padrão."
yield IO.read(caminho)
monitor:
abrir("nao-existe.csv")
handle Error as e:
out e.message