Portabilidade Bash
Orientações para escrever scripts portáteis e compatíveis com POSIX e entender quando usar recursos específicos do bash.
Seleção de Shebang
Use #!/usr/bin/env bash para scripts Bash
#!/usr/bin/env bash
Por quê: Busca bash no PATH, funciona em sistemas onde o bash pode estar em locais diferentes.
Use #!/bin/sh para scripts POSIX
#!/bin/sh
Por quê: Portabilidade máxima quando recursos do bash não são necessários. Em muitos sistemas, /bin/sh é dash ou outro shell POSIX.
Caminho direto quando necessário
#!/bin/bash
Use apenas quando: Requisitos do sistema garantem o local do bash ou a política de segurança exige caminhos absolutos.
Matriz de recursos POSIX vs Bash
| Recurso | POSIX | Bash | Recomendação |
|---|---|---|---|
[[ ]] | Não | Sim | Use [ ] para POSIX |
(( )) | Não | Sim | Use [ ] com -eq etc. |
| Arrays | Não | Sim | Use parâmetros posicionais ou arquivos |
local | Parcial | Sim | Geralmente seguro |
${var,,} | Não | 4+ | Use tr para POSIX |
<<< | Não | Sim | Use `echo |
=~ regex | Não | Sim | Use grep ou expr |
source | Não | Sim | Use . (comando ponto) |
function f() | Não | Sim | Use apenas f() |
$'...' | Não | Sim | Use printf |
{1..10} | Não | Sim | Use seq ou loop while |
Padrões compatíveis com POSIX
Condicionais
# POSIX - use [ ] com aspas adequadas
if [ -f "$file" ]; then
echo "Arquivo existe"
fi
# Comparação de strings
if [ "$var" = "value" ]; then
echo "Correspondência"
fi
# Comparação numérica
if [ "$num" -gt 10 ]; then
echo "Maior"
fi
# Condições compostas
if [ -f "$file" ] && [ -r "$file" ]; then
echo "Arquivo legível"
fi
Conversão de maiúsculas/minúsculas (POSIX)
# Minúsculas
minusculo=$(echo "$string" | tr '[:upper:]' '[:lower:]')
# Maiúsculas
maiusculo=$(echo "$string" | tr '[:lower:]' '[:upper:]')
Operações de substring (POSIX)
# Obter substring - use expr ou cut
substr=$(expr "$string" : '.\{3\}\(.\{5\}\)') # caracteres 4-8
substr=$(echo "$string" | cut -c4-8)
# Comprimento da string
length=$(expr length "$string")
length=${#string} # Isso é, na verdade, POSIX
Leitura de arquivos (POSIX)
# Linha por linha
while IFS= read -r line; do
echo "$line"
done < "$file"
# Ler arquivo inteiro (sem cat)
content=$(cat "$file") # cat é POSIX
Substituição de comando
# Sintaxe moderna (preferida mesmo em POSIX)
result=$(comando)
# Sintaxe legada (evitar)
result=`comando`
# Aninhamento (por que a moderna é melhor)
result=$(echo $(date)) # Claro
result=`echo \`date\`` # Pesadelo de escapamento
Recursos específicos do Bash que valem a pena usar
Quando a portabilidade não é necessária, esses recursos do bash melhoram a qualidade do código:
Teste estendido [[ ]]
# Correspondência de padrão
[[ "$file" == *.txt ]]
# Correspondência de regex
[[ "$input" =~ ^[0-9]+$ ]]
# Sem preocupações com divisão de palavras
[[ -f $file ]] # Aspas opcionais (ainda recomendado)
# Operadores lógicos internos
[[ -f "$file" && -r "$file" ]]
Arrays
# Arrays indexados
declare -a files=()
files+=("um.txt")
files+=("dois.txt")
for f in "${files[@]}"; do
process "$f"
done
# Arrays associativos (Bash 4+)
declare -A config
config[host]="localhost"
config[port]="8080"
Expansão de parâmetros
# Valor padrão
"${var:-default}"
# Conversão de maiúsculas/minúsculas (Bash 4+)
"${var,,}" # minúsculas
"${var^^}" # maiúsculas
# Substring
"${var:0:10}" # primeiros 10 caracteres
"${var: -5}" # últimos 5 caracteres
# Busca/substituição
"${var//old/new}"
Here Strings
# Bash
read -r var <<< "string de entrada"
# Equivalente POSIX
var=$(echo "string de entrada")
Substituição de processo
# Bash - comparar duas saídas de comando
diff <(sort arquivo1) <(sort arquivo2)
# Equivalente POSIX (com arquivos temporários)
sort arquivo1 > /tmp/ordenado1
sort arquivo2 > /tmp/ordenado2
diff /tmp/ordenado1 /tmp/ordenado2
Detectando o tipo de shell
# Verificar se está rodando no bash
if [ -n "${BASH_VERSION:-}" ]; then
echo "Rodando no Bash"
fi
# Verificar versão do bash para recursos
if [ "${BASH_VERSINFO[0]:-0}" -ge 4 ]; then
echo "Bash 4+ disponível"
fi
# Detecção genérica de shell
case "${SHELL##*/}" in
bash) echo "bash" ;;
zsh) echo "zsh" ;;
*) echo "outro" ;;
esac
Funções utilitárias portáteis
Guia de decisão de portabilidade
Use POSIX quando:
- O script roda em sistemas mínimos (contêineres, embarcados)
- O destino inclui dash, ash ou busybox sh
- Compatibilidade máxima é necessária
- O script faz parte da inicialização do sistema
Use Bash quando:
- Os sistemas de destino têm garantia de ter bash
- Arrays, arrays associativos ou regex são necessários
- Manipulação complexa de strings é necessária
- Clareza do código é significativamente melhorada
- Recursos interativos são necessários
Armadilhas comuns de portabilidade
echo vs printf
# Problemático - comportamento varia
echo -n "sem nova linha"
echo -e "com\ttabs"
# Portátil
printf '%s' "sem nova linha"
printf 'com\ttabs\n'
Atribuição de variável
# Funciona em todos os lugares
var="valor"
# Pode falhar em alguns shells
var = "valor" # Espaços ao redor de = estão errados
Export com atribuição
# POSIX - comandos separados
var="valor"
export var
# Bash/moderno - combinado (funciona na maioria dos lugares)
export var="valor"
Operações semelhantes a arrays sem arrays
# Use parâmetros posicionais
set -- "item1" "item2" "item3"
for item in "$@"; do
echo "$item"
done
# Ou divisão baseada em IFS
items="item1:item2:item3"
IFS=':' read -r item1 item2 item3 <<EOF
$items
EOF