Truques para criar aliases e funções bash persistentes por projeto
1. Fundamentos da Persistência por Projeto
1.1. Diferença entre aliases globais (~/.bashrc) e locais por projeto
Aliases globais definidos em ~/.bashrc ou ~/.bash_aliases são carregados uma vez no início da sessão e permanecem disponíveis em qualquer diretório. Isso funciona bem para comandos universais como ll='ls -la', mas se torna problemático quando você trabalha em múltiplos projetos com contextos diferentes. Por exemplo, um alias run='npm start' pode ser adequado para um projeto Node.js, mas não para um projeto Python que usa run='python app.py'.
Aliases por projeto resolvem esse conflito carregando definições específicas apenas quando você está no diretório do projeto correspondente.
1.2. Por que scripts .env ou .bash_project são superiores a variáveis temporárias
Variáveis temporárias definidas manualmente com export se perdem ao fechar o terminal. Scripts como .env ou .bash_project oferecem persistência controlada: são arquivos versionáveis, podem conter lógica condicional e são facilmente compartilháveis entre membros da equipe via repositório.
1.3. Estrutura de diretório recomendada
Duas abordagens principais:
- Centralizada:
~/.project-aliases/projeto-x.sh— mantém todos os aliases em um local, mas requer manutenção manual de referências. - Distribuída:
.bash_aliasesdentro do projeto — cada projeto carrega seu próprio arquivo, facilitando o versionamento.
A abordagem distribuída é mais recomendada por ser autocontida e portátil.
2. Técnica do Arquivo .env com Fonte Automática
2.1. Criando um arquivo .env com aliases específicos
Crie um arquivo .env na raiz do projeto:
# .env do projeto
alias build='docker build -t meu-projeto .'
alias test='pytest tests/'
alias run='python main.py'
function deploy() {
echo "Fazendo deploy da branch $(git branch --show-current)..."
git push origin main
}
2.2. Usando PROMPT_COMMAND para carregar automaticamente
Adicione ao seu ~/.bashrc:
__load_project_env() {
local env_file=".env"
if [ -f "$env_file" ] && [ "$PWD" != "$__LAST_PWD" ]; then
source "$env_file"
__LAST_PWD="$PWD"
echo "[PROJETO] Aliases carregados de $PWD/$env_file"
fi
}
PROMPT_COMMAND="__load_project_env; $PROMPT_COMMAND"
2.3. Script auto_source.sh que percorre diretórios pai
Para projetos aninhados, um script que busca arquivos nos diretórios pai:
# auto_source.sh
__find_and_source() {
local dir="$PWD"
while [ "$dir" != "/" ]; do
if [ -f "$dir/.env" ]; then
source "$dir/.env"
return
fi
dir=$(dirname "$dir")
done
}
PROMPT_COMMAND="__find_and_source; $PROMPT_COMMAND"
3. Funções Bash com Detecção de Diretório
3.1. Função project_alias que verifica $PWD
project_alias() {
local cmd="$1"
case "$PWD" in
*/projeto-x/*)
case "$cmd" in
build) docker-compose build ;;
test) npm test ;;
*) echo "Comando desconhecido para este projeto" ;;
esac
;;
*/projeto-y/*)
case "$cmd" in
build) mvn clean install ;;
test) mvn test ;;
*) echo "Comando desconhecido para este projeto" ;;
esac
;;
*)
echo "Nenhum projeto reconhecido neste diretório"
;;
esac
}
3.2. Uso de case ou if para mapear pastas
function deploy() {
if [[ "$PWD" == *"/projeto-x"* ]]; then
echo "Deploy do projeto X..."
docker push meu-projeto:latest
elif [[ "$PWD" == *"/projeto-y"* ]]; then
echo "Deploy do projeto Y..."
git push heroku main
else
echo "Projeto não reconhecido para deploy"
fi
}
3.3. Exemplo prático: deploy() condicional
function docker_up() {
if [ -f "docker-compose.yml" ]; then
docker-compose up -d
else
echo "docker-compose.yml não encontrado em $PWD"
return 1
fi
}
4. Gerenciamento com Git Hooks e .git/info/
4.1. Hook post-checkout para recarregar aliases
Crie .git/hooks/post-checkout:
#!/bin/bash
# Recarrega aliases após trocar de branch
if [ -f ".bash_aliases" ]; then
source ".bash_aliases"
echo "[GIT] Aliases recarregados do branch $(git branch --show-current)"
fi
4.2. Hook post-merge para atualizar funções
#!/bin/bash
# Atualiza funções após merge que modifica .bash_project
if git diff HEAD@{1} --name-only | grep -q ".bash_project"; then
source ".bash_project"
echo "[GIT] Funções atualizadas após merge"
fi
4.3. Armazenando aliases em .git/info/aliases
Para não poluir o repositório, use o diretório .git/info/ (não versionado):
# .git/info/aliases
alias gs='git status'
alias gc='git commit'
alias gp='git push'
E no ~/.bashrc:
if [ -f ".git/info/aliases" ]; then
source ".git/info/aliases"
fi
5. Integração com direnv e Ferramentas Similares
5.1. Configuração básica do direnv
Instale o direnv e adicione ao ~/.bashrc:
eval "$(direnv hook bash)"
5.2. Exemplo de .envrc com aliases
# .envrc
layout python
alias run='flask run'
alias test='pytest -v'
function lint() {
flake8 src/ tests/
}
O direnv carrega automaticamente ao entrar no diretório e descarrega ao sair.
5.3. Alternativa leve: função cd personalizada
cd() {
builtin cd "$@" && [ -f ".project_functions" ] && source ".project_functions"
}
6. Funções com Namespace para Evitar Conflitos
6.1. Prefixo de projeto
# Em vez de 'build', use 'proj_x_build'
function proj_x_build() { docker build -t proj-x .; }
function proj_x_test() { pytest tests/; }
6.2. Função wrapper com verificação de conflito
safe_alias() {
local name="$1"
local cmd="$2"
if type "$name" &>/dev/null; then
echo "AVISO: Alias '$name' já existe globalmente. Usando '${name}_local'"
alias "${name}_local=$cmd"
else
alias "$name=$cmd"
fi
}
6.3. Uso de declare -f e type para evitar sobrescrita
if ! declare -f build &>/dev/null; then
function build() { echo "Build padrão"; }
fi
7. Automação de Carga com trap e PROMPT_COMMAND
7.1. Script que adiciona verificação ao PROMPT_COMMAND
__check_project_aliases() {
local current_dir="$PWD"
if [ "$current_dir" != "$__LAST_PROJECT_DIR" ]; then
__unload_project_aliases
if [ -f "$current_dir/.bash_aliases" ]; then
source "$current_dir/.bash_aliases"
__LAST_PROJECT_DIR="$current_dir"
__LAST_ALIASES_HASH=$(md5sum "$current_dir/.bash_aliases" 2>/dev/null)
fi
fi
}
PROMPT_COMMAND="__check_project_aliases; $PROMPT_COMMAND"
7.2. Função __load_project_aliases executada a cada prompt
__load_project_aliases() {
local alias_file="$PWD/.bash_aliases"
local current_hash=$(md5sum "$alias_file" 2>/dev/null | cut -d' ' -f1)
if [ -f "$alias_file" ] && [ "$current_hash" != "$__ALIAS_HASH" ]; then
source "$alias_file"
__ALIAS_HASH="$current_hash"
echo "[ALIAS] Carregado: $alias_file"
fi
}
7.3. Cache de md5sum para evitar recarregar desnecessariamente
__ALIAS_CACHE=""
__load_with_cache() {
local file="$PWD/.bash_aliases"
local new_hash=$(md5sum "$file" 2>/dev/null)
if [ -f "$file" ] && [ "$new_hash" != "$__ALIAS_CACHE" ]; then
source "$file"
__ALIAS_CACHE="$new_hash"
fi
}
8. Dicas de Depuração e Manutenção
8.1. Listando aliases carregados
alias | grep -E "^alias (build|test|run)="
Ou para listar apenas os do projeto atual:
alias -p | grep "$PWD"
8.2. Logging simples
function __log_alias_load() {
echo "$(date '+%Y-%m-%d %H:%M:%S') [PROJETO] $PWD - Aliases carregados" >> /tmp/project_aliases.log
}
8.3. Remoção segura ao sair do diretório
__unload_project_aliases() {
if [ -n "$__PROJECT_ALIASES_LOADED" ]; then
unalias build test run 2>/dev/null
unset -f deploy lint 2>/dev/null
unset __PROJECT_ALIASES_LOADED
echo "[ALIAS] Aliases do projeto descarregados"
fi
}
Referências
- Bash Reference Manual: Aliases — Documentação oficial sobre criação e gerenciamento de aliases no Bash.
- direnv: Environment Variables for the Terminal — Ferramenta para carregar variáveis e aliases automaticamente ao entrar em diretórios específicos.
- Git Hooks Documentation — Guia completo sobre hooks do Git, incluindo post-checkout e post-merge.
- PROMPT_COMMAND and Bash Prompt Magic — Tutorial avançado sobre uso de PROMPT_COMMAND para automação no Bash.
- Advanced Bash-Scripting Guide: Functions — Referência detalhada sobre criação de funções bash com detecção de contexto.
- Stack Overflow: How to source a file on directory change — Discussão técnica sobre técnicas de carregamento automático de aliases por diretório.