Blocos de Código Shell & Bash no Org Babel
O Org Babel oferece suporte abrangente a scripts de shell em shells Unix e POSIX: Bash, Zsh, Sh, Fish, Dash, Csh, Ksh e Ash.
Blocos de shell transformam o Org Mode em um caderno interativo de DevOps, runbook e console de administração de sistemas, capaz de execução local, processamento assíncrono em segundo plano e administração remota de servidores via SSH com TRAMP.
1. Requisitos e Configuração
1.1 Configuração do Emacs
Ao contrário de outras linguagens Babel que exigem módulos separados, todos os shells são governados por uma única biblioteca Babel (ob-shell). Você só precisa de (shell . t) em seu org-babel-load-languages:
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(shell . t)))
(Nota: Antes do Org 8.2, isso era chamado de sh. Use shell em todas as versões modernas do Org).
1.2 Especificando o Shell
Especifique o nome exato do binário do shell como a tag de linguagem do bloco (#+BEGIN_SRC <shell>):
# Bash:
#+BEGIN_SRC bash
echo "Executando em $BASH_VERSION"
#+END_SRC
# Zsh:
#+BEGIN_SRC zsh
echo "Executando em Zsh $ZSH_VERSION"
#+END_SRC
# POSIX / Dash (execução mais rápida e leve):
#+BEGIN_SRC dash
echo "Executando em dash"
#+END_SRC
# Fish shell:
#+BEGIN_SRC fish
set greeting "Olá do fish"
echo $greeting
#+END_SRC
O binário do shell especificado deve estar acessível em seu $PATH do sistema.
2. Modos de Execução: Isolado, Sessão e Assíncrono
2.1 Subshell Isolado (Padrão, :session none)
Cada bloco executa em um novo processo subshell temporário. Variáveis de ambiente e diretórios de trabalho não são preservados:
#+BEGIN_SRC bash :results output
echo "PID: $$"
export WORKSPACE="/tmp/build"
#+END_SRC
#+BEGIN_SRC bash :results output
# $WORKSPACE está vazio aqui porque isso executa em um novo processo subshell:
echo "PID: $$, WORKSPACE: $WORKSPACE"
#+END_SRC
2.2 Sessões Interativas (:session <nome>)
Executa dentro de um buffer comint persistente do Emacs (ex.: *dev-session*). Variáveis, funções e diretórios de trabalho persistem entre vários blocos:
#+BEGIN_SRC bash :session *dev-session* :results output
export CLUSTER_ID="prod-east-01"
cd /var/log
#+END_SRC
#+BEGIN_SRC bash :session *dev-session* :results output
# Usa o estado definido no bloco anterior:
echo "Implementando em $CLUSTER_ID a partir de $(pwd)"
#+END_SRC
#+RESULTS:
: Implementando em prod-east-01 a partir de /var/log
2.3 Execução Assíncrona Não Bloqueante (:async yes)
Scripts de longa duração (backups, compilações de contêineres, treinamento de modelos) congelam o Emacs quando executados de forma síncrona. Adicione :async yes para executá-los em segundo plano sem travar o editor!
Regras:
:asyncrequer:session <nome>.- Durante a execução, o Babel insere um token UUID temporário no bloco
#+RESULTS:. - Quando a execução termina em segundo plano, o UUID é automaticamente substituído pela saída padrão do comando.
#+BEGIN_SRC bash :session *bg-job* :async yes :results output
echo "Iniciando backup pesado do banco de dados em $(date)..."
sleep 5
echo "Backup concluído com sucesso em $(date)!"
#+END_SRC
#+RESULTS:
: Iniciando backup pesado do banco de dados em Sun May 18 14:00:00 UTC 2025...
: Backup concluído com sucesso em Sun May 18 14:00:05 UTC 2025!
Para executar de forma síncrona em uma sessão, use :async no.
3. Argumentos de Cabeçalho Específicos para Shell
O Org Babel fornece três argumentos de cabeçalho exclusivos, adaptados especificamente para operações de shell:
3.1 :stdin <nome-do-elemento> (Entrada Padrão)
Envia elementos nomeados do Org (tabelas, texto bruto ou resultados de outros blocos de código) para o stdin do comando shell:
# Entrada de texto bruto:
#+NAME: raw_server_payload
{"node": "alpha", "cores": 8, "status": "active"}
# Envia o payload diretamente para o jq:
#+BEGIN_SRC bash :stdin raw_server_payload :results output
jq -r '.node + " has " + (.cores|tostring) + " cores"'
#+END_SRC
#+RESULTS:
: alpha has 8 cores
(Nota: Blocos usando :stdin são avaliados em um subshell temporário, ignorando :session).
3.2 :cmdline <args> (Argumentos de Linha de Comando)
Passa argumentos posicionais ($1, $2, $@) para o corpo do script:
#+BEGIN_SRC bash :cmdline "staging" 8080 :results output
ENV="$1"
PORT="$2"
echo "Iniciando servidor na porta $PORT no modo $ENV..."
#+END_SRC
#+RESULTS:
: Iniciando servidor na porta 8080 no modo staging...
⚠️ Aviso de Segurança: Argumentos :cmdline são expandidos diretamente pelo shell hospedeiro. Não passe strings não confiáveis ou sem escape para evitar injeção de comando.
3.3 :shebang <diretiva> (Scripts Independentes e Saída Limpa)
Força o bloco a ser escrito em um arquivo executável temporário e executado via sua diretiva shebang:
#+BEGIN_SRC bash :shebang "#!/usr/bin/env bash" :results output
for i in {1..3}; do
echo "Passo $i"
done
#+END_SRC
Por que :shebang é essencial:
Em blocos :session de várias linhas sem um shebang, as linhas são enviadas linha por linha para o shell interativo, o que pode produzir prompts indesejados (ex.: bash-5.1$ ) nos resultados. Especificar :shebang ou executar sem uma sessão produz uma saída completamente limpa.
4. Administração Remota de Servidores via TRAMP (:dir)
Você pode executar comandos em servidores remotos, máquinas virtuais ou contêineres Docker especificando um caminho TRAMP em :dir:
:dir /ssh:<usuário>@<host>:<caminho-remoto>
:dir /docker:<id-do-contêiner>:<caminho>
:dir /sudo::/etc
Exemplo: Fluxo de Trabalho de Sysadmin Remoto
# Verifica uso de disco e memória em um servidor remoto:
#+BEGIN_SRC bash :dir /ssh:[email protected]:/tmp :results output
hostname
uptime
df -h / | awk 'NR==2 {print "Uso do Disco Raiz: " $5}'
#+END_SRC
#+RESULTS:
: bastion.example.com
: 14:22:05 up 42 days, 3:18, 1 user, load average: 0.12, 0.08, 0.05
: Uso do Disco Raiz: 48%
Sessões Remotas
Combine :dir com :session para manter uma conexão SSH ativa em vários blocos sem reconectar a cada etapa:
#+BEGIN_SRC bash :dir /ssh:[email protected]:/home/admin :session *remote-bastion* :results output
WORKDIR=$(mktemp -d)
cd "$WORKDIR"
pwd
#+END_SRC
5. Formatos de Resultado e Extração de Dados Tabulares
5.1 Saída Delimitada por Tabulação para Tabela Org (:results output table)
Quando comandos shell emitem saída tabular separada por tabulações ou estruturada, o Babel os analisa em tabelas Org nativas:
#+BEGIN_SRC bash :results output table
printf "PID\tUser\tCommand\n"
printf "101\troot\tsystemd\n"
printf "405\tpostgres\tpostgres\n"
printf "892\tnginx\tnginx: worker\n"
#+END_SRC
#+RESULTS:
| PID | User | Command |
|-----+----------+---------------|
| 101 | root | systemd |
| 405 | postgres | postgres |
| 892 | nginx | nginx: worker |
5.2 Direcionando a Saída para um Link de Arquivo (:results file :file <caminho>)
#+BEGIN_SRC bash :results file :file reports/sysinfo.txt
uname -a > reports/sysinfo.txt
cat /etc/os-release >> reports/sysinfo.txt
#+END_SRC
#+RESULTS:
[[file:reports/sysinfo.txt]]
6. Passando Variáveis e Arrays Bash (:var)
6.1 Escalares e Strings
#+BEGIN_SRC bash :var port=8000 host="localhost" :results output
echo "Conectando a $host na porta $port..."
#+END_SRC
6.2 Arrays 1D do Bash
O Bash suporta parâmetros de array 1D definidos com sintaxe de lista:
#+BEGIN_SRC bash :results output :var servers='("web-01" "web-02" "db-01")'
echo "Total de servidores: ${#servers[@]}"
for s in "${servers[@]}"; do
echo "Pingando $s..."
done
#+END_SRC
#+RESULTS:
: Total de servidores: 3
: Pingando web-01...
: Pingando web-02...
: Pingando db-01...
7. Armadilhas Comuns e Melhores Práticas de Segurança
| Armadilha | Causa | Solução |
|---|---|---|
| Emacs trava em comando SSH ou ffmpeg | Comando tenta ler stdin do subshell | Redirecione stdin explicitamente (</dev/null) ou use ssh -n. |
Caracteres de prompt bash-5.1$ indesejados nos resultados | Eco linha por linha interativo em :session | Adicione :shebang #!/usr/bin/env bash ou avalie fora da sessão. |
| Expansão inadvertida de variáveis em heredocs | Usar <<EOF permite que o shell expanda $VARS prematuramente | Use delimitador com aspas <<"EOF" para manter o texto literal e evitar expansão precoce. |
Sintaxe não-POSIX falhando em blocos sh | #+BEGIN_SRC sh mapeia para /bin/sh (geralmente dash no Debian/Ubuntu) | Use #+BEGIN_SRC bash se seu script usar bashismos ([[, ((, arrays). |
Injeção de comando via :cmdline | Argumentos do shell avaliados literalmente | Cite os argumentos com cuidado ou passe dados via :stdin ou :var. |
| Saída de bloco assíncrono não aparecendo | :async yes requer uma :session ativa | Sempre forneça um :session <nome> ao usar :async yes. |
8. Exemplo Completo de Runbook DevOps
#+TITLE: Runbook de Saúde do Serviço de Produção
#+PROPERTY: header-args:bash :session *runbook* :results output
* Etapa 1: Configuração de Verificação de Saúde
#+NAME: cluster_nodes
| Hostname | Função |
|-----------------+-----------|
| prod-app-01.lan | backend |
| prod-app-02.lan | backend |
| prod-db-01.lan | armazenamento |
* Etapa 2: Sondar Nós do Cluster
#+BEGIN_SRC bash :var nodes=cluster_nodes :colnames yes :results output table
printf "Host\tFunção\tStatus\n"
for row in "${nodes[@]}"; do
# Analisa os dados da linha separados por tabulação/espaço:
host=$(echo "$row" | awk '{print $1}')
role=$(echo "$row" | awk '{print $2}')
# Sonda rápida de ping:
if ping -c 1 -W 1 "$host" > /dev/null 2>&1; then
status="ONLINE"
else
status="OFFLINE"
fi
printf "%s\t%s\t%s\n" "$host" "$role" "$status"
done
#+END_SRC
#+RESULTS:
| Host | Função | Status |
|-----------------+-----------------+--------|
| prod-app-01.lan | backend | ONLINE |
| prod-app-02.lan | backend | ONLINE |
| prod-db-01.lan | armazenamento | ONLINE |
* Etapa 3: Executar Manutenção de Serviço Assincronamente
#+BEGIN_SRC bash :session *runbook* :async yes
echo "Iniciando purga de cache em rotação nos nós..."
sleep 4
echo "Purga de cache concluída com sucesso."
#+END_SRC