Variáveis & Strings
Variáveis & Atribuição
Variáveis Bash são strings sem tipo por padrão; aritmética funciona se o valor for numérico. NUNCA coloque espaços ao redor de = em atribuições — 'name = Alice' tenta rodar um comando chamado name. Use $(...) para substituição de comando (preferido em vez de backticks). export torna uma variável disponível para processos filhos; readonly a torna imutável. Variáveis de ambiente (exportadas) são herdadas por scripts e programas que você inicia.
# no spaces around = in assignments
name="Alice"
age=30
PI=3.14
active=true
# use variables with $ prefix
echo "Hello, $name!"
echo "Age: $age"
# command substitution: capture command output
today=$(date +%Y-%m-%d)
files=$(ls | wc -l)
echo "Today is $today, $files files"
# readonly and environment variables
readonly MAX=100
export PATH="$PATH:/opt/bin"
env | grep PATHExpansão de Parâmetro de String
Expansão de parâmetro é um dos recursos mais poderosos do Bash, delimitada por ${...}. ${#var} dá o comprimento; ${var:offset:length} extrai substrings (offset negativo conta do final — note o espaço antes do -). ,, e ^^ convertem case (Bash 4+). :- fornece defaults sem definir a var; := define-a como efeito colateral; :? aborta com um erro — ótimo para verificações de argumentos obrigatórios em scripts.
s="Hello World"
echo ${#s} # length: 11
echo ${s:0:5} # substring: Hello
echo ${s:6} # from index 6: World
echo ${s: -5} # last 5 chars: World
# case conversion (Bash 4+)
echo ${s,,} # lowercase: hello world
echo ${s^^} # uppercase: HELLO WORLD
echo ${s^l} # capitalize first if 'l': Hello World
# default values
echo ${name:-Guest} # use Guest if name unset
echo ${name:=Guest} # assign Guest if unset, then use
echo ${name:?required} # error and exit if unsetBusca & Substituição em Strings
O padrão / substitui; // substitui todos; # corresponde no início; % corresponde no final. ## e %% são gananciosos (correspondência mais longa) enquanto # e % são não-gananciosos. Esses são essenciais para manipulação de path: ${path##*/} extrai o basename (como o comando basename), e ${path%/*} extrai o diretório (como dirname). Dominar isso evita muitas chamadas a comandos externos como sed e cut.
s="Hello World Hello"
# replace first occurrence
echo ${s/Hello/Hi} # Hi World Hello
# replace all occurrences
echo ${s//Hello/Hi} # Hi World Hi
# replace at beginning / end
echo ${s/#Hello/Hi} # Hi World Hello (prefix)
echo ${s/%Hello/Hi} # Hello World Hello (no suffix match)
# delete patterns (replace with empty)
echo ${s//Hello/} # World
path="/usr/local/bin/app"
echo ${path##*/} # app (longest prefix)
echo ${path%/*} # /usr/local/bin (shortest suffix)
# extract filename and extension
file="report.tar.gz"
echo ${file%.*} # report.tar (remove ext)
echo ${file##*.} # gz (keep ext)Quoting & Caracteres Especiais
Aspas simples preservam tudo literalmente — sem expansão de variável, sem processamento de escape (você não pode sequer incluir uma aspa simples dentro de aspas simples). Aspas duplas permitem expansão de $, backtick e \ enquanto preservam espaços e caracteres especiais — quase sempre prefira aspas duplas ao redor de variáveis para evitar bugs de word-splitting e globbing. Use $'...' para escapes ANSI-C como \n e \t.
# single quotes: literal, no expansion
echo 'Hello $USER' # Hello $USER
# double quotes: expansion happens
echo "Hello $USER" # Hello alice
# escape special chars with backslash
echo "Price: \\$5.00" # Price: $5.00
echo 'It\'s a test' # It's a test (escape inside single)
# concatenate strings
a="Hello"
b="World"
c="$a, $b!"
echo $c # Hello, World!
# multi-line string
msg="Line 1
Line 2
Line 3"
echo "$msg"Read & Entrada do Usuário
read captura entrada do usuário. -p mostra um prompt, -t define um timeout (retorna non-zero), -a lê em um array. Ao ler arquivos linha por linha, sempre use 'IFS= read -r' — IFS= preserva whitespace inicial/final, e -r desativa o escape de backslash para que backslashes sejam preservados literalmente. Este é o padrão seguro canônico para processar arquivos linha por linha.
# read a single value
echo -n "Enter your name: "
read name
echo "Hi, $name!"
# read multiple values
read -p "First and last: " first last
# read with timeout (seconds)
read -t 5 -p "Quick! " answer || echo "too slow"
# read into an array
echo "Enter colors:"
read -a colors
echo "First: ${colors[0]}"
# read line by line from a file
while IFS= read -r line; do
echo ">>> $line"
done < file.txtArrays
Arrays Indexados
Arrays Bash são indexados em zero. @ e * expandem para todos os elementos — sempre os coloque entre aspas ("${arr[@]}") para lidar com elementos com espaços corretamente. ${#arr[@]} dá a contagem. += anexa. unset deixa um espaço nos índices; para reindexar, use arr=("${arr[@]}"). Slicing com ${arr[@]:start:count} retorna um subconjunto.
# create and populate
fruits=("apple" "banana" "cherry")
fruits[3]="date"
# access elements
echo ${fruits[0]} # apple
echo ${fruits[@]} # all: apple banana cherry date
echo ${fruits[*]} # same as @ in most contexts
# count and slice
echo ${#fruits[@]} # count: 4
echo ${fruits[@]:1:2} # elements 1-2: banana cherry
# append
fruits+=("elderberry")
# iterate
for f in "${fruits[@]}"; do
echo "- $f"
done
# delete an element
unset fruits[1] # removes "banana" (gap remains)Arrays Associativos (Maps)
Arrays associativos (declare -A) mapeiam chaves string a valores, como dicionários em outras linguagens — disponíveis no Bash 4+. Use ${!arr[@]} para obter chaves e ${arr[@]} para valores. O teste -v verifica se uma chave existe. Diferente de arrays indexados, as chaves são strings arbitrárias, então colocar chaves com espaços entre aspas é essencial. Eles exigem Bash 4+ (macOS vem com Bash 3 por padrão).
# must declare before use (Bash 4+)
declare -A ages
# key-value assignment
ages[alice]=30
ages[bob]=25
ages["carol smith"]=28
# access by key
echo ${ages[alice]} # 30
echo ${ages[bob]} # 25
# all keys and values
echo ${!ages[@]} # keys: alice bob carol smith
echo ${ages[@]} # values: 30 25 28
echo ${#ages[@]} # count: 3
# iterate keys
for name in "${!ages[@]}"; do
echo "$name is ${ages[$name]}"
done
# check if key exists
if [[ -v ages[alice] ]]; then
echo "alice exists"
fiPadrões de Iteração de Array
Existem três estilos principais de iteração: por valor (for x in "${arr[@]}"), por índice (for i in "${!arr[@]}"), e estilo C. A forma por índice é útil quando você precisa da posição. Sempre coloque "${arr[@]}" entre aspas para que elementos contendo espaços sejam preservados como itens únicos. Construir arrays com += em um loop é a maneira idiomática de acumular resultados.
arr=(one two three four five)
# iterate by value
for item in "${arr[@]}"; do
echo "$item"
done
# iterate by index
for i in "${!arr[@]}"; do
echo "[$i] = ${arr[$i]}"
done
# C-style indexed loop
for ((i=0; i<${#arr[@]}; i++)); do
echo "${arr[$i]}"
done
# map/transform: build a new array
upper=()
for w in "${arr[@]}"; do
upper+=("$(echo $w | tr a-z A-Z)")
done
echo "${upper[@]}"Array a partir de Saída de Comando
mapfile/readarray (Bash 4+) lê linhas em um array de uma só vez — muito mais rápido que um loop while para arquivos grandes. Para dividir uma string delimitada, defina IFS e use read -ra. Para nomes de arquivo (que podem conter espaços ou newlines), sempre use find -print0 com read -d '' para dividir em bytes nulos — o único delimitador seguro para nomes de arquivo. A substituição de processo < <(...) alimenta o loop sem um subshell.
# split a string into an array
csv="a,b,c,d"
IFS=',' read -ra parts <<< "$csv"
echo ${parts[2]} # c
# read lines of a file into an array
mapfile -t lines < file.txt
echo "${lines[0]}" # first line
echo "${#lines[@]}" # line count
# alternative: readarray (same as mapfile)
readarray -t lines2 < file.txt
# from command output (words split on IFS)
files=($(ls *.txt))
echo "${files[@]}"
# safer: read null-delimited output
while IFS= read -r -d '' f; do
files+=("$f")
done < <(find . -name "*.txt" -print0)Variáveis Especiais
Essas variáveis especiais são essenciais para scripting. $0 é o path do script; $1-$9 são argumentos posicionais (use ${10} para dois dígitos). Sempre coloque "$@" entre aspas para preservar argumentos com espaços — "$*" os junta em uma string. $? dá o exit status (0 = sucesso) e é a base de toda verificação de erro. $$ e $! são úteis para arquivos PID e gerenciamento de processos. shift descarta $1 e desloca o restante para baixo.
# positional parameters
echo $0 # script name
echo $1 # first argument
echo $# # argument count
echo $@ # all arguments (separate words)
echo "$@" # all arguments (preserves quoting)
echo $* # all arguments (single string)
# process and shell info
echo $$ # current shell PID
echo $! # PID of last background command
echo $? # exit status of last command (0 = success)
echo $- # current shell option flags
echo $_ # last argument of previous command
# shift arguments
echo "$1 $2" # a b
shift
echo "$1 $2" # b c
# iterate all args safely
for arg in "$@"; do
echo "arg: $arg"
doneControle de Fluxo & Testes
If / Elif / Else
[ ] é o comando de teste POSIX (portável mas limitado); [[ ]] é a versão aprimorada do Bash que suporta correspondência de padrões (== com wildcards), regex (=~), operadores && / ||, e não precisa de aspas nas variáveis. Prefira [[ ]] em scripts Bash. Sempre coloque variáveis entre aspas em [ ] para evitar erros de sintaxe com valores vazios ou contendo espaços. Os testes -f, -r, -d, -e verificam existência e atributos de arquivo.
# basic if
if [ "$age" -ge 18 ]; then
echo "adult"
elif [ "$age" -ge 13 ]; then
echo "teen"
else
echo "child"
fi
# modern [[ ]] test (Bash-specific, safer)
if [[ $name == "Alice" ]]; then
echo "hi Alice"
fi
# pattern matching in [[ ]]
if [[ $file == *.txt ]]; then
echo "text file"
fi
# regex matching
if [[ $email =~ ^[a-z]+@[a-z]+.[a-z]+$ ]]; then
echo "valid email"
fi
# multiple conditions
if [[ -f file.txt && -r file.txt ]]; then
echo "readable file"
fiOperadores de Teste
Testes de arquivo (-f, -d, -r, etc.) verificam atributos do sistema de arquivos. Testes de string usam = e != (ou == em [[ ]]); -z verifica vazio, -n para não-vazio. Comparações de inteiro usam -eq, -ne, -lt, -gt, -le, -ge — NÃO use < > para inteiros em [ ] (eles redirecionam!). Em [[ ]] e (( )) você pode usar os familiares operadores < > <= >=. Memorizar esses operadores é essencial para escrever condicionais corretos.
# file tests
[ -f file ] # exists and is regular file
[ -d dir ] # exists and is directory
[ -e path ] # exists (any type)
[ -r file ] # readable
[ -w file ] # writable
[ -x file ] # executable
[ -s file ] # exists and is non-empty
[ file1 -nt file2 ] # newer than
[ file1 -ot file2 ] # older than
# string tests
[ -z "$s" ] # empty string
[ -n "$s" ] # non-empty string
[ "$a" = "$b" ] # equal (use == in [[ ]])
[ "$a" != "$b" ] # not equal
# integer tests
[ $n -eq 5 ] # equal
[ $n -ne 5 ] # not equal
[ $n -lt 5 ] # less than
[ $n -gt 5 ] # greater than
[ $n -le 5 ] # less or equal
[ $n -ge 5 ] # greater or equalComando Case
case é o equivalente Bash do switch — corresponde um valor contra padrões glob. | separa alternativas. ;; encerra um branch (como break). O padrão *) é o default. Padrões suportam wildcards (*, ?, [abc]) mas não regex completo. case é mais limpo que longas cadeias if-elif para despachar em um único valor, e é a maneira padrão de construir subcomandos de linha de comando (start/stop/restart).
case $1 in
start)
echo "Starting service..."
systemctl start app
;;
stop)
echo "Stopping service..."
systemctl stop app
;;
restart)
$0 stop
$0 start
;;
status)
systemctl status app
;;
--help|-h)
echo "Usage: $0 {start|stop|restart|status}"
;;
*)
echo "Unknown command: $1" >&2
exit 1
;;
esac
# pattern matching in case
case $file in
*.jpg|*.png) echo "image" ;;
*.mp3|*.wav) echo "audio" ;;
*.txt) echo "text" ;;
*) echo "other" ;;
esacAritmética & (( ))
$(( )) avalia expressões aritméticas e retorna o resultado. Dentro de (( )), nomes de variável não precisam de $ — apenas use o nome. (( )) como comando retorna exit status 0 (true) se o resultado for non-zero, tornando-o ideal para condições numéricas. Bash só faz aritmética de inteiros; para ponto flutuante use bc ou awk. Operadores: + - * / % ** e operadores de comparação e bitwise estilo C todos funcionam.
# arithmetic expansion
x=5
y=$((x + 3))
echo $y # 8
echo $((x * 2)) # 10
echo $((2 ** 10)) # 1024 (exponent)
echo $((17 % 5)) # 2 (modulo)
echo $((17 / 5)) # 3 (integer division)
# (( )) condition: no $ needed, returns exit status
x=10
if (( x > 5 && x < 20 )); then
echo "in range"
fi
# increment / decrement
((x++)) # post-increment
((x--)) # post-decrement
((++x)) # pre-increment
# bitwise operators
echo $((5 & 3)) # 1 (AND)
echo $((5 | 2)) # 7 (OR)
echo $((5 << 1)) # 10 (left shift)Curto-Circuito & Ternário
&& e || são operadores de curto-circuito que também servem como controle de fluxo conciso: 'cmd1 && cmd2' roda cmd2 apenas se cmd1 tiver sucesso; 'cmd1 || cmd2' roda cmd2 apenas se cmd1 falhar. Isso substitui if-else simples em uma linha. O padrão 'A && B || C' imita um ternário mas é sutilmente bugado se B pode falhar — para código robusto, use um if real. Os dois pontos (:) é um comando no-op útil para branches vazios.
# && and || as short-circuit control flow
[[ -f file.txt ]] && echo "exists"
[[ -f file.txt ]] || echo "missing"
# combined: do this OR fail
mkdir -p build && cd build && make || exit 1
# ternary-like (Bash doesn't have a real ternary)
result=$([[ $x -gt 0 ]] && echo "positive" || echo "non-positive")
# default value via &&
echo ${name:-"anonymous"}
# run command, fallback on failure
ping -c1 host && echo "up" || echo "down"
# null command as no-op
if condition; then
: # do nothing
fiLoops & Iteração
For Loops
O loop for itera sobre uma lista de palavras. Expansão de chaves {1..5} gera sequências (com passo opcional {start..end..step}). O for estilo C for ((init; cond; update)) é melhor para contadores numéricos. Ao iterar arquivos com *.txt, globs sem aspas expandem com segurança, mas se nenhum arquivo corresponder você obtém o literal '*.txt' — ative shopt -s nullglob para obter uma lista vazia.
# iterate a list
for fruit in apple banana cherry; do
echo "$fruit"
done
# iterate an array
colors=("red" "green" "blue")
for c in "${colors[@]}"; do
echo "Color: $c"
done
# range with brace expansion
for i in {1..5}; do
echo "Number: $i"
done
# range with step
for i in {0..20..2}; do echo $i; done # 0 2 4 ... 20
# C-style for loop
for ((i=0; i<5; i++)); do
echo "Iteration $i"
done
# iterate command output
for file in *.txt; do
echo "Processing $file"
doneLoops While & Until
while roda enquanto a condição tiver sucesso (exit 0); until roda até ter sucesso — são opostos. O padrão 'while read' é a maneira canônica de processar arquivos linha por linha; sempre use 'IFS= read -r' para segurança. Note que fazer pipe para um loop while o roda em um subshell, então mudanças de variável não persistem — use substituição de processo '< <(cmd)' quando precisar manter variáveis.
# while: runs while condition is true
count=0
while [ $count -lt 5 ]; do
echo "Count: $count"
((count++))
done
# read lines from a file
while IFS= read -r line; do
echo "Line: $line"
done < input.txt
# infinite loop with break
while true; do
read -p "Quit? (y/n) " ans
[[ $ans == "y" ]] && break
done
# until: runs until condition is true (opposite of while)
n=0
until [ $n -ge 3 ]; do
echo "n=$n"
((n++))
done
# while reading from a pipeline
seq 1 5 | while read n; do
echo "got $n"
doneBreak, Continue & Select
break sai do loop (break N sai de N loops aninhados); continue pula para a próxima iteração. select cria um menu numerado interativo a partir de uma lista — ele faz loop até ser quebrado. Essas declarações de controle funcionam em loops for, while e until. A forma break N é essencial para escapar de loops profundamente aninhados mas deve ser usada com moderação pois pode tornar o código mais difícil de seguir.
# break exits the loop
for i in {1..10}; do
[[ $i -eq 5 ]] && break
echo $i # prints 1 2 3 4
done
# continue skips to next iteration
for i in {1..6}; do
(( i % 2 == 0 )) && continue
echo $i # prints 1 3 5 (odd only)
done
# break out of nested loops
for i in 1 2 3; do
for j in a b c; do
[[ $j == "b" ]] && break 2 # break both loops
echo "$i$j"
done
done
# select menu
select opt in "Start" "Stop" "Quit"; do
case $opt in
Start) echo "starting" ;;
Stop) echo "stopping" ;;
Quit) break ;;
*) echo "invalid" ;;
esac
doneIterando Arquivos com Segurança
Fazer parse da saída do ls é um bug clássico — nomes de arquivo com espaços, newlines ou caracteres especiais quebram. Use globs do shell (*.txt) diretamente em loops for, e coloque a variável entre aspas. Para buscas recursivas ou complexas, use find -print0 com pipe para 'read -d ''' — bytes nulos são o único delimitador seguro para nomes de arquivo. Ative nullglob para que globs sem correspondência produzam uma lista vazia em vez do padrão literal.
# BAD: breaks on filenames with spaces
for f in $(ls *.txt); do
cat "$f"
done
# GOOD: glob expands safely
for f in *.txt; do
[[ -f "$f" ]] || continue
cat "$f"
done
# BEST: handle spaces, newlines, and no matches
shopt -s nullglob dotglob
for f in *.txt; do
echo "Processing: $f"
done
# find with -print0 and while read -d ''
while IFS= read -r -d '' f; do
echo "Found: $f"
done < <(find . -type f -name "*.txt" -print0)
# process files modified today
for f in $(find . -mtime -1 -print); do
echo "Recent: $f"
doneRanges & Geração de Sequências
Expansão de chaves {a..b} gera sequências em tempo de parse — rápido e integrado. Suporta letras, números, zero-padding e passos ({start..end..step}). seq é um comando externo com mais opções de formatação (-f para estilo printf). A expansão de chaves também cria múltiplos argumentos: echo file{1..3}.{txt,log} gera 6 nomes de arquivo. Prefira expansão de chaves para ranges simples; use seq quando precisar de formatação customizada.
# brace expansion (not a loop, but generates lists)
echo {1..10} # 1 2 3 4 5 6 7 8 9 10
echo {a..e} # a b c d e
echo {01..10} # zero-padded: 01 02 ... 10
echo file{1..3}.txt # file1.txt file2.txt file3.txt
# seq command
seq 1 5 # 1 2 3 4 5
seq 1 2 10 # 1 3 5 7 9 (step 2)
seq -f "%03d" 1 5 # 001 002 003 004 005
# use seq in a for loop
for i in $(seq 1 5); do
echo $i
done
# generate a list of dates
for d in $(seq -f "2024-01-%02g" 1 31); do
echo "$d"
doneFunções
Definindo & Chamando Funções
Funções em Bash são definidas com name() ou function name. Chame-as pelo nome com argumentos separados por espaço (sem parênteses). SEMPRE use 'local' para variáveis dentro de funções para evitar poluir o escopo global — sem isso, atribuições vazam. Funções Bash não podem retornar valores diretamente (return é um exit status 0-255); para retornar uma string, faça echo dela e capture com $().
# two equivalent syntaxes
greet() {
echo "Hello, $1!"
}
function greet2() {
echo "Hi, $1!"
}
# call with arguments (no parens)
greet "Alice" # Hello, Alice!
greet2 "Bob" # Hi, Bob!
# local variables (function-scoped)
counter() {
local count=0
((count++))
echo $count
}
# return a value via echo + command substitution
add() {
echo $(($1 + $2))
}
result=$(add 3 4) # 7Argumentos & Parâmetros
Dentro de uma função, $1, $@, $# referem-se aos argumentos da função, não aos do script — eles sombreiam os parâmetros de nível de script. $FUNCNAME contém o nome da função (útil para debugar). shift dentro de uma função afeta apenas os parâmetros da função. Para passar os argumentos do script para uma função, use func "$@". Esse sombreamento é o motivo pelo qual funções são blocos de construção reutilizáveis.
# all positional params work inside functions
show_args() {
echo "Function name: $FUNCNAME"
echo "First arg: $1"
echo "Second arg: $2"
echo "All args: $@"
echo "Arg count: $#"
}
show_args a b c
# Function name: show_args
# First arg: a
# Second arg: b
# All args: a b c
# Arg count: 3
# pass all script args to a function
process "$@"
# shift within a function (local to it)
parse() {
while (($#)); do
echo "arg: $1"
shift
done
}Valores de Retorno & Exit Status
O 'return' do Bash só define um exit status (0-255), então funções servem como testes booleanos. Para retornar uma string, faça echo dela e capture com $(). Para múltiplos valores, use um nameref (local -n) — Bash 4.3+ — que permite à função atribuir à variável do chamador por nome. Essa é a maneira mais limpa de retornar dados complexos. Evite usar variáveis globais para valores de retorno pois isso torna as funções não-reentrantes.
# return sets exit status (0-255), not a value
is_even() {
if (($1 % 2 == 0)); then
return 0 # true/success
else
return 1 # false/failure
fi
}
# use as a condition
if is_even 4; then
echo "4 is even"
fi
# chain with && and ||
is_even 4 && echo "yes" || echo "no"
# return a string via echo (capture with $())
to_upper() {
echo "${1^^}"
}
UP=$(to_upper hello) # HELLO
# return multiple values via global or nameref
set_pair() {
local -n _r=$1
_r=(10 20)
}
set_pair result
echo "${result[@]}" # 10 20Escopo de Variáveis
Variáveis em Bash são globais por padrão — mesmo quando atribuídas dentro de uma função! Essa é uma fonte comum de bugs. Sempre use 'local' para variáveis internas de função. 'declare -g' cria explicitamente uma global de dentro de uma função. Recursão funciona mas é lenta no Bash (cada chamada faz fork de um subshell para $()); use-a com moderação. O exemplo de fatorial mostra o padrão: variáveis locais + chamada recursiva + aritmética.
# global by default
x=1
modify() {
x=2 # changes the GLOBAL x!
local y=3 # local to function
echo "inside: x=$x y=$y"
}
modify
echo "outside: x=$x" # x=2 (modified!)
echo "outside: y=$y" # empty (y is local)
# declare -g for global inside a function
set_global() {
declare -g NEW_VAR=42
}
set_global
echo $NEW_VAR # 42
# recursion with local
factorial() {
local n=$1
if (( n <= 1 )); then
echo 1
else
local prev=$(factorial $((n - 1)))
echo $((n * prev))
fi
}
echo $(factorial 5) # 120Bibliotecas & Sourcing
source (ou .) executa um arquivo no shell atual, então suas funções e variáveis ficam disponíveis — é assim que você constrói bibliotecas reutilizáveis. Um padrão comum é um utils.sh com funções auxiliares que scripts fazem source. Diferente de executar um script (que roda em um subshell), código sourced pode modificar o ambiente do chamador. É assim que .bashrc e .bash_profile funcionam — eles são sourced na inicialização do shell.
# utils.sh — a reusable library
#!/bin/bash
log() {
echo "[$(date +%H:%M:%S)] $*" >&2
}
die() {
log "ERROR: $*"
exit 1
}
confirm() {
read -p "$1 [y/N] " ans
[[ $ans =~ ^[Yy]$ ]]
}
# main.sh — use the library
source ./utils.sh # or . ./utils.sh
log "Starting script"
confirm "Continue?" || die "aborted"
log "Done"
# source vs execute
# source: runs in current shell (shares variables)
# ./script.sh: runs in a subshell (isolated)Processamento de Texto
grep — Busca de Padrões
grep encontra linhas que correspondem a um padrão. -i ignora case, -v inverte, -n mostra números de linha, -r recursa, -E usa regex estendido (como +, |, {}). -A/-B/-C mostram linhas de contexto ao redor das correspondências — inestimável para entender erros. Use --include para filtrar tipos de arquivo durante a busca recursiva. grep retorna exit status 0 se uma correspondência é encontrada, tornando-o útil em condições: if grep -q pattern file; then...
# basic search
grep "error" app.log
grep -i "error" app.log # case-insensitive
grep -v "debug" app.log # invert (non-matching lines)
grep -c "error" app.log # count matches
grep -n "error" app.log # line numbers
grep -w "error" app.log # whole word only
# recursive search
grep -rn "TODO" src/ # recursive + line numbers
grep -rl "TODO" src/ # files with matches only
# extended regex (or use egrep)
grep -E "error|warn|fatal" log
grep -E "^[0-9]{4}-" log # lines starting with date
# context lines
grep -A 2 "error" log # 2 lines after
grep -B 2 "error" log # 2 lines before
grep -C 2 "error" log # 2 lines before and after
# search multiple files
grep "function" *.js --include="*.js" -r .sed — Editor de Stream
sed edita streams de texto não-interativamente. O comando s substitui; g torna global. -i edita in-place (sempre teste sem -i primeiro!). O delimitador pode ser qualquer caractere — use | ou # para paths para evitar escapar barras. sed processa linha por linha, então operações multi-linha precisam do comando N ou hold space. Para edições complexas, awk ou perl podem ser mais claros. Sempre coloque scripts sed entre aspas para evitar expansão do shell.
# substitute (replace)
sed 's/old/new/' file.txt # first occurrence per line
sed 's/old/new/g' file.txt # all occurrences (global)
sed 's/old/new/3' file.txt # 3rd occurrence only
sed 's/old/new/gi' file.txt # global + case-insensitive
# in-place editing
sed -i 's/foo/bar/g' file.txt # edit file in place
sed -i.bak 's/foo/bar/g' file.txt # keep a backup
# delete lines
sed '/^#/d' file.txt # delete comment lines
sed '/^$/d' file.txt # delete blank lines
sed '5d' file.txt # delete line 5
sed '5,10d' file.txt # delete lines 5-10
# print specific lines
sed -n '10,20p' file.txt # print lines 10-20
sed -n '/pattern/p' file.txt # print matching lines
# use a different delimiter for paths
sed 's|/usr/local|/opt|g' paths.txt
# multiple commands
sed -e 's/a/A/g' -e 's/b/B/g' file.txtawk — Processamento de Colunas
awk é uma mini linguagem de programação para dados colunares. $1, $2... são campos; $0 é a linha inteira; $NF é o último campo. -F define o separador de entrada; OFS define o de saída. BEGIN roda antes do processamento, END depois. NR é o número de registro (linha); NF é a contagem de campos. awk é ideal para processamento de CSV/TSV, análise de logs e geração de relatórios — muito mais poderoso que cut para qualquer coisa além de extração simples.
# print columns (default separator: whitespace)
awk '{print $1}' file.txt # first column
awk '{print $1, $3}' file.txt # columns 1 and 3
awk '{print $NF}' file.txt # last column
awk -F: '{print $1}' /etc/passwd # split on :
# filter and print
awk '$3 > 100' file.txt # lines where col 3 > 100
awk '$1 == "ERROR" {print $2}' log # col 2 of ERROR lines
awk 'NR == 5' file.txt # 5th line only
awk 'NR >= 10 && NR <= 20' file.txt # lines 10-20
# BEGIN and END blocks
awk 'BEGIN {print "Start"} {sum += $1} END {print sum}' nums.txt
# field separator and output
awk -F, 'BEGIN {OFS="|"} {print $1, $2}' csv.txt
# count lines matching a pattern
awk '/error/' file.txt # like grep
awk '/error/ {count++} END {print count}' log
# built-in variables: NR (row), NF (fields), FS, OFS
awk '{print NR, NF, $0}' file.txtcut, tr, sort & uniq
cut é extração simples de campo/caractere — rápido mas limitado (sem suporte a quoting). tr traduz caracteres (ótimo para conversão de case ou troca de delimitador) e -d deleta. sort ordena linhas (-n numérico, -r reverso, -k campo). uniq só remove duplicatas ADJACENTES, então sempre faça pipe através do sort primeiro. uniq -c com sort -rn é o padrão clássico para análise de frequência: 'sort | uniq -c | sort -rn' mostra as linhas mais comuns.
# cut: extract fields or characters
cut -d: -f1 /etc/passwd # field 1, delimiter :
cut -c1-5 file.txt # characters 1-5
cut -d, -f2,3 data.csv # fields 2 and 3
# tr: translate or delete characters
echo "Hello" | tr 'a-z' 'A-Z' # HELLO
echo "a,b,c" | tr ',' ' ' # a b c
echo "hello" | tr -d 'l' # heo (delete)
tr -s ' ' < file.txt # squeeze repeated spaces
# sort lines
sort file.txt # alphabetical
sort -n nums.txt # numeric
sort -rn nums.txt # reverse numeric
sort -t: -k3 -n /etc/passwd # by 3rd field, numeric
sort -u file.txt # sort + unique
# uniq: filter or count adjacent duplicates
sort file.txt | uniq # unique lines
sort file.txt | uniq -c # count occurrences
sort file.txt | uniq -d # only duplicates
sort file.txt | uniq -u # only unique linesPipes & Redirecionamento
Pipes conectam stdout ao stdin, construindo pipelines poderosos. > redireciona stdout (>> anexa), 2> redireciona stderr, &> captura ambos. Here-docs (<<EOF) alimentam strings multi-linha; colocar o delimitador entre aspas ('EOF') desativa a expansão de variável. Substituição de processo <(cmd) trata a saída de um comando como um arquivo temporário — essencial para alimentar loops while sem um subshell, e para comandos como diff que esperam arquivos.
# pipe: chain commands (stdout of one -> stdin of next)
cat file | grep "x" | sort | uniq -c | sort -rn | head
# redirect stdout
echo "hello" > file.txt # overwrite
echo "world" >> file.txt # append
# redirect stderr
command 2> error.log # stderr to file
command 2>&1 # stderr to stdout
command > all.log 2>&1 # both to same file
command &> all.log # both (Bash shortcut)
# stdin from file
command < input.txt
# here-string
grep "x" <<< "some text with x"
# here-document (multi-line input)
cat << EOF
Line 1
Line 2 with $VAR expansion
EOF
# quoted delimiter disables expansion
cat << 'EOF'
Literal $VAR no expansion
EOF
# process substitution
diff <(ls dir1) <(ls dir2) # compare outputs
while read line; do echo "$line"; done < <(grep x file)Operações de Arquivo & Diretório
find — Busca de Arquivos
find é a ferramenta de busca de arquivos mais poderosa. -name corresponde a globs (use -iname para case-insensitive); -type filtra por f/d/l; -mtime/-mmin filtram por tempo de modificação (- = dentro de, + = mais antigo que); -size filtra por tamanho. -exec roda um comando em cada resultado; {} é o nome do arquivo, \; roda por arquivo, + os agrupa. -delete é mais seguro que -exec rm para deleção. Sempre teste find sem -delete primeiro!
# search by name
find . -name "*.js"
find . -iname "*.JS" # case-insensitive
find / -name "*.conf" 2>/dev/null # suppress permission errors
# search by type
find . -type f -name "*.txt" # files only
find . -type d -name src # directories only
find . -type l # symlinks
# search by time and size
find . -mtime -7 # modified < 7 days ago
find . -mtime +30 # modified > 30 days ago
find . -mmin -60 # modified < 60 min ago
find . -size +10M # larger than 10MB
find . -size -1k # smaller than 1KB
find . -empty # empty files/dirs
# act on results (-exec)
find . -name "*.log" -exec rm {} \;
find . -name "*.js" -exec wc -l {} +
find . -type f -exec chmod 644 {} \;
# safe deletion with -delete
find /tmp -type f -mtime +7 -deletePermissões & Propriedade
Permissões são três trincas: owner, group, others. Cada dígito é r(4)+w(2)+x(1), então 755 = rwxr-xr-x. chmod muda permissões; chown muda owner/group. Notação simbólica (u+x, g-w) é mais clara para mudanças incrementais. umask define permissões padrão para novos arquivos (subtraído de 666 para arquivos, 777 para dirs). Para servidores web, 755 para dirs e 644 para arquivos é padrão; nunca use 777 em produção.
# view permissions
ls -l file.txt
# -rw-r--r-- 1 user group 1024 Jan 1 10:00 file.txt
# ^^^ ^^^ ^^^
# owner group others
# chmod: change permissions
chmod 755 script.sh # rwxr-xr-x
chmod 644 file.txt # rw-r--r--
chmod +x script.sh # add execute for all
chmod u+x,g-w file.txt # symbolic: user +x, group -w
chmod -R 755 directory/ # recursive
# chown: change owner
chown alice file.txt
chown alice:developers file.txt
chown -R alice:developers project/
# umask: default permissions for new files
umask # show current (e.g. 022)
umask 077 # new files: 600, dirs: 700
# numeric permission reference:
# 7=rwx 6=rw 5=rx 4=r 3=wx 2=w 1=x 0=noneCopiar, Mover, Remover & Link
cp -r copia diretórios recursivamente; -i pede antes de sobrescrever (mais seguro); -p preserva timestamps e permissões. mv é tanto move quanto rename. rm -rf é perigoso — deleta recursivamente sem pedir; sempre verifique o path. Hard links apontam para o mesmo inode (mesmo arquivo, não pode cruzar sistemas de arquivos ou linkar dirs); symlinks são referências de path (podem linkar qualquer coisa, mas quebram se o alvo se move). Use ln -s para symlinks.
# copy
cp file.txt backup.txt
cp -r src/ dest/ # recursive (directories)
cp -i file.txt dest/ # interactive (prompt before overwrite)
cp -u file.txt dest/ # update (only if newer)
cp -p file.txt dest/ # preserve attributes
# move / rename
mv old.txt new.txt
mv file.txt /other/dir/
mv -i file.txt dest/ # prompt before overwrite
# remove
rm file.txt
rm -r directory/ # recursive
rm -f file.txt # force (no error if missing)
rm -rf node_modules/ # force + recursive (DANGER)
# links
ln target hardlink # hard link (same inode)
ln -s target symlink # symbolic link (path reference)
ln -sf target symlink # force recreate symlink
# view link target
readlink symlink
readlink -f symlink # canonical absolute pathArquivos & Compressão
tar agrupa arquivos em um archive; gzip/bzip2/xz o comprimem. As flags: c=create, x=extract, t=list, f=file, z=gzip, j=bzip2, J=xz, v=verbose. .tar.gz é o padrão Unix; .zip é comum no Windows. bzip2 comprime melhor que gzip mas é mais lento; xz é o melhor mas o mais lento. Use -C para extrair para um diretório específico. A flag -k preserva o original ao comprimir.
# tar + gzip (most common)
tar -czf archive.tar.gz dir/ # create
tar -xzf archive.tar.gz # extract
tar -xzf archive.tar.gz -C /opt/ # extract to /opt
tar -tzf archive.tar.gz # list contents
# tar + bzip2 (better compression)
tar -cjf archive.tar.bz2 dir/
tar -xjf archive.tar.bz2
# zip / unzip
zip -r archive.zip dir/
unzip archive.zip
unzip archive.zip -d /target
# gzip / gunzip (single files)
gzip file.txt # -> file.txt.gz
gunzip file.txt.gz # -> file.txt
gzip -k file.txt # keep original
# xz (best compression)
tar -cJf archive.tar.xz dir/
tar -xJf archive.tar.xz
# mnemonic: eXtract, Create, List, File, gZip, bZip2, xzConteúdo de Arquivo & Comparação
cat despeja o arquivo inteiro; head/tail mostram as extremidades; tail -f faz stream de atualizações ao vivo (essencial para logs). less é um pager interativo com busca (/) e navegação. diff compara arquivos; -u produz o formato unificado usado pelo patch. comm compara dois arquivos ordenados e mostra linhas únicas de cada ou comuns a ambos — útil para comparar listas. Sempre ordene as entradas antes do comm, pois ele exige arquivos ordenados.
# view file contents
cat file.txt # entire file
head -n 20 file.txt # first 20 lines
tail -n 20 file.txt # last 20 lines
tail -f app.log # follow (live updates)
less file.txt # pager (q to quit, / to search)
# line/word/char count
wc file.txt # lines words bytes
wc -l file.txt # lines only
wc -w file.txt # words only
# compare files
diff file1.txt file2.txt
diff -u file1.txt file2.txt # unified format (for patches)
diff -r dir1/ dir2/ # recursive
# patch
diff -u old new > change.patch
patch < change.patch
patch -R < change.patch # reverse
# find duplicate lines across files
comm -12 <(sort a) <(sort b) # lines in both
comm -23 <(sort a) <(sort b) # only in a
comm -13 <(sort a) <(sort b) # only in bGerenciamento de Processos & Sinais
Jobs em Background & Controle de Jobs
Acrescentar & roda um comando em background, retornando imediatamente. jobs lista jobs ativos; fg/bg os move entre foreground e background. Ctrl+Z suspende o job em foreground (envia SIGTSTP). wait bloqueia até os jobs em background terminarem — essencial para scripts que lançam trabalho paralelo. disown remove um job da tabela de jobs do shell para que ele continue rodando após você deslogar (diferente do nohup, funciona em jobs já em execução).
# run in background (append &)
sleep 100 &
# [1] 12345 (job 1, PID 12345)
# list jobs
jobs
jobs -l # with PIDs
# bring to foreground / send to background
fg # foreground the current job
fg %1 # foreground job 1
bg %2 # background job 2 (continue running)
# suspend a running foreground job: Ctrl+Z
# then resume in background:
bg
# wait for background jobs
sleep 5 &
wait # wait for all background jobs
wait $! # wait for the last background job
wait 12345 # wait for specific PID
# disown: detach from shell (survives logout)
long_task &
disown %1kill & Sinais
kill envia sinais a processos. SIGTERM (padrão) pede ao processo para sair graciosamente — ele pode limpar. SIGKILL (-9) é forçado e imediato; o processo não pode capturá-lo ou ignorá-lo, então pode deixar recursos em mau estado. Sempre tente SIGTERM primeiro, espere, depois SIGKILL apenas se necessário. killall/pkill matam por nome. pkill -f corresponde à linha de comando completa (mais flexível). Use kill -l para listar todos os nomes de sinais.
# send signals to processes
kill 12345 # SIGTERM (graceful, default)
kill -15 12345 # SIGTERM explicitly
kill -9 12345 # SIGKILL (force, cannot be caught)
kill -HUP 12345 # SIGHUP (reload config)
# kill by name
killall nginx # kill all nginx processes
pkill -f "python app.py" # match full command line
# common signals
# SIGTERM (15) - graceful termination (default)
# SIGKILL (9) - force kill (cannot be caught/ignored)
# SIGINT (2) - interrupt (Ctrl+C)
# SIGHUP (1) - hangup (often reload config)
# SIGSTOP (19) - pause (cannot be caught)
# SIGCONT (18) - resume
# SIGTSTP (20) - terminal stop (Ctrl+Z)
# list all signals
kill -l
# priority: try SIGTERM first, SIGKILL as last resort
kill 12345; sleep 2; kill -9 12345 2>/dev/nulltrap — Tratamento de Sinais
trap registra comandos para rodar quando o script recebe um sinal — essencial para limpeza. O pseudo-sinal EXIT dispara em qualquer saída (normal, erro, ou morto), tornando-o perfeito para remover arquivos temporários. Sempre defina traps antes de criar os recursos que eles limpam. Padrão comum: trap cleanup EXIT INT TERM. trap '' SIGNAL o ignora; trap - SIGNAL restaura o padrão. É assim que scripts robustos garantem limpeza mesmo quando interrompidos.
# clean up on exit (EXIT signal)
trap 'rm -f $TMPFILE' EXIT
TMPFILE=$(mktemp)
echo "working with $TMPFILE"
# when script exits (normally or via error), TMPFILE is removed
# handle Ctrl+C (SIGINT)
trap 'echo "Caught Ctrl+C, exiting"; exit 130' INT
# handle multiple signals
cleanup() {
echo "Cleaning up..."
rm -rf "$WORKDIR"
exit
}
trap cleanup INT TERM EXIT
# ignore a signal
trap '' INT # ignore Ctrl+C
trap - INT # restore default behavior
# reload on SIGHUP
trap 'echo "reloading config"; source config.sh' HUP
# list current traps
trap -pInspeção de Processos
ps mostra um snapshot de processos; top/htop mostram em tempo real. pstree exibe a hierarquia pai-filho. pgrep encontra PIDs por nome (mais seguro que ps|grep). lsof -i :PORT encontra qual processo usa uma porta — essencial para debugar erros de 'porta em uso'. ss (socket statistics) é o substituto moderno do netstat. O padrão ps aux | grep é onipresente mas pgrep é mais limpo. Use --sort para encontrar os que mais consomem recursos.
# list processes
ps aux # all processes (BSD style)
ps -ef # all processes (System V style)
ps -ef | grep nginx # find nginx processes
# interactive viewer (like Task Manager)
top # real-time, all processes
htop # nicer interactive viewer (install separately)
# process tree
pstree # tree view
pstree -p # with PIDs
# find process by name/port
pgrep -f "node app" # print PIDs matching
pgrep -fl "node" # full command line
# who's listening on a port
lsof -i :8080 # process using port 8080
ss -tlnp # all listening TCP ports
netstat -tlnp # legacy alternative
# process resource usage
top -b -n 1 | head # snapshot
ps aux --sort=-%mem | head # top memory usersnohup, disown & tmux
nohup faz um processo ignorar SIGHUP para que sobreviva ao logout — a saída vai para nohup.out. disown alcança o mesmo para um job em background já em execução. setsid inicia um processo em uma nova sessão, desanexando-o totalmente. Para trabalho interativo de longa duração, tmux ou screen são melhores: eles mantêm uma sessão de terminal completa viva à qual você pode reanexar depois, então até editores de texto sobrevivem a desconexões. É assim que sysadmins gerenciam servidores remotos.
# nohup: run immune to hangups (survives logout)
nohup ./long_script.sh &
# output goes to nohup.out by default
nohup ./server.sh > server.log 2>&1 &
# disown: detach an already-running job
./long_task &
disown %1
# setsid: start in a new session
setsid ./daemon.sh &
# tmux: terminal multiplexer (persistent sessions)
tmux # start new session
tmux new -s work # named session
tmux ls # list sessions
tmux attach -t work # reattach
# inside tmux: Ctrl+B then D to detach
# processes keep running after detach
# screen: alternative to tmux
screen -S work
screen -ls
screen -r work
# check if a process is running
pgrep -x nginx && echo "running" || echo "stopped"Scripting & Tópicos Avançados
Shebang & Estrutura de Script
O shebang (#!) diz ao kernel qual interpretador usar. #!/usr/bin/env bash é o mais portável. Um script bem estruturado começa com 'set -euo pipefail' para segurança, define usage(), e faz parse de argumentos com getopts (para flags curtas) ou um loop manual (para flags longas). OPTIND rastreia o próximo argumento; shift além das opções parseadas para que $1 seja o primeiro argumento posicional. Essa estrutura torna scripts robustos e amigáveis ao usuário.
#!/usr/bin/env bash
# ^ portable shebang (finds bash via PATH)
# #!/bin/bash # absolute path (common)
# #!/usr/bin/bash # on some systems
# #!/bin/sh # POSIX sh (most portable)
set -euo pipefail # strict mode (see below)
# script metadata
readonly PROGNAME=$(basename "$0")
readonly VERSION="1.0.0"
usage() {
cat << EOF
Usage: $PROGNAME [OPTIONS] FILE
Options:
-h, --help Show this help
-v, --version Show version
-o OUTPUT Output file
EOF
}
# parse options with getopts
while getopts "hvo:" opt; do
case $opt in
h) usage; exit 0 ;;
v) echo $VERSION; exit 0 ;;
o) output=$OPTARG ;;
*) usage; exit 1 ;;
esac
done
shift $((OPTIND - 1))Strict Mode (set -euo pipefail)
set -e sai imediatamente se qualquer comando falhar (retorna non-zero) — captura erros cedo. set -u trata referências a variáveis não definidas como erros. pipefail faz um pipeline retornar non-zero se QUALQUER comando nele falhar (por padrão, apenas o status do último comando importa). Juntos eles capturam a maioria dos bugs. Use 'cmd || true' para permitir falhas esperadas, e 'if ! cmd' para verificações explícitas. Alguns comandos (grep, test) retornam non-zero legitimamente, então envolva-os.
# the 'strict mode' trio
set -e # exit on error (any non-zero status)
set -u # error on unset variable
set -o pipefail # pipeline fails if any command fails
# combined: set -euo pipefail
# example: without -e, errors are silently ignored
mkdir /root/nope # fails (permission denied)
echo "still running" # this runs anyway!
# with set -e, the script exits at the failure
set -e
mkdir /root/nope # script exits here
echo "never reached"
# handle expected failures
set -e
if ! grep -q "x" file; then
echo "not found" # grep returning 1 won't kill script
fi
# temporarily disable -e
set +e
risky_command
set -e
# || true to ignore a command's failure
mkdir -p dir || trueTécnicas de Debugging
set -x (xtrace) imprime cada comando antes da execução — a ferramenta #1 de debugging. PS4 customiza o prompt (adicionar números de linha ajuda a localizar problemas). bash -n verifica sintaxe sem rodar. trap ERR roda um comando quando qualquer erro ocorre, perfeito para registrar onde as coisas deram errado. Para scripts complexos, envolva funções suspeitas com set -x/+x para rastrear apenas aquela parte. Combine com set -e para parar no primeiro erro e inspecionar.
# trace execution: print each command before running
set -x # turn on
set +x # turn off
bash -x script.sh # run with tracing from the start
# verbose: print input lines as read
set -v
# combine: -xv for full detail
bash -xv script.sh
# print commands with line numbers
export PS4='+ ${BASH_SOURCE}:${LINENO}: '
set -x
# check syntax without executing
bash -n script.sh # syntax check only
# step through interactively
bash -x script.sh 2>&1 | less
# debug a specific function
debug_func() {
set -x
actual_func "$@"
set +x
}
# use trap ERR to catch errors
trap 'echo "Error on line $LINENO" >&2' ERRAritmética & Matemática
Bash só faz aritmética de inteiros com $(( )). Para ponto flutuante, faça pipe de expressões para bc (scale=N define casas decimais) ou use awk. bc -l carrega a biblioteca matemática (sqrt, sin, cos, etc.). $RANDOM dá um inteiro pseudo-aleatório 0-32767; para aleatoriedade criptográfica use /dev/urandom. O comando (( )) suporta +=, -=, *=, /=, %= e ++/-- como C. Para matemática séria, Python ou awk são escolhas melhores.
# integer arithmetic
echo $((5 + 3)) # 8
echo $((10 / 3)) # 3 (integer division)
echo $((10 % 3)) # 1 (modulo)
echo $((2 ** 8)) # 256 (exponent)
# floating point with bc
echo "scale=2; 10/3" | bc # 3.33
echo "3.14 * 2" | bc # 6.28
result=$(echo "scale=4; $a / $b" | bc)
# floating point with awk
awk 'BEGIN {printf "%.2f\n", 10/3}' # 3.33
awk "BEGIN {print $a * $b}"
# math functions with bc -l
echo "scale=4; s(0)" | bc -l # 0 (sine, radians)
echo "scale=4; sqrt(16)" | bc -l # 4.0000
# random numbers
echo $RANDOM # 0-32767
echo $((RANDOM % 100)) # 0-99
head -c 4 /dev/urandom | od -An -tu4 # 32-bit random
# increment/decrement
n=5
((n++)); echo $n # 6
((n+=10)); echo $n # 16Regex & Correspondência de Padrões
Bash tem três sistemas de padrões: globs (*.txt, ?, [abc]) para nomes de arquivo, globs estendidos (extglob: !(), @(), *()) para correspondência mais complexa, e regex ERE (=~ em [[ ]]). =~ captura grupos em BASH_REMATCH (índice 0 = correspondência completa, 1+ = grupos). Regex usa sintaxe ERE (como grep -E). Ative extglob para negação e alternância poderosas em padrões de nome de arquivo. Regex é um dos recursos mais úteis do Bash para validação de entrada.
# glob patterns (filename matching)
*.txt # any .txt file
file?.txt # single char: file1.txt, fileA.txt
file[0-9].txt # digit: file0.txt ... file9.txt
file[!0-9].txt # NOT a digit
# extended globs (shopt -s extglob)
shopt -s extglob
echo !(*.bak) # everything except .bak files
echo @(*.jpg|*.png) # jpg or png
echo *(foo) # zero or more "foo"
# regex with =~ in [[ ]]
if [[ $email =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then
echo "valid email"
fi
# capture groups with BASH_REMATCH
if [[ $date =~ ^([0-9]{4})-([0-9]{2})-([0-9]{2})$ ]]; then
echo "Year: ${BASH_REMATCH[1]}"
echo "Month: ${BASH_REMATCH[2]}"
echo "Day: ${BASH_REMATCH[3]}"
fi
# case-insensitive regex
shopt -s nocasematch
if [[ $s =~ ^hello ]]; then echo "starts with hello"; figrep Aprofundado
Padrões Básicos do grep
grep busca texto usando padrões. -i ignora case; -w corresponde palavras inteiras (evita corresponder 'errors' ao buscar 'error'); -v inverte (linhas não correspondentes); -c conta; -n mostra números de linha; -l lista apenas nomes de arquivo com correspondências; -h suprime o prefixo de nome de arquivo ao buscar múltiplos arquivos. Por padrão, grep usa Basic Regular Expressions (BRE) onde metacaracteres precisam de escape com backslash. Use -E para Extended Regex (sintaxe mais limpa) ou -F para strings fixas (sem regex, mais rápido para buscas literais).
# Basic search (literal string)
grep "error" logfile.txt
# Case-insensitive
grep -i "error" logfile.txt
# Whole word match
grep -w "error" logfile.txt
# Invert match (lines NOT matching)
grep -v "debug" logfile.txt
# Count matches
grep -c "error" logfile.txt
# Show line numbers
grep -n "error" logfile.txt
# Multiple files with filename prefix
grep "error" *.log
# Suppress filename prefix (single file behavior)
grep -h "error" *.log
# Only show filenames with matches
grep -l "error" *.loggrep com Regex
grep -E (ou egrep) usa Extended Regular Expressions com sintaxe mais limpa: +, ?, |, () funcionam sem escape. ^ e $ ancoram ao início/fim da linha. [] define classes de caracteres; {} especifica repetição. -P ativa regex compatível com Perl (PCRE) com recursos como \d, \w, lookaheads — mas isso é específico do GNU e não portável. Para regex complexo, considere ripgrep (rg) que é mais rápido e usa sintaxe tipo PCRE por padrão. Sempre coloque padrões entre aspas para evitar interpretação do shell de caracteres especiais como $ e *.
# Extended regex (-E or egrep)
grep -E "error|warning|fatal" logfile.txt
# Anchors: ^ start, $ end
grep -E "^ERROR:" logfile.txt # lines starting with ERROR:
grep -E "completed$" logfile.txt # lines ending with completed
# Character classes
grep -E "[0-9]{4}-[0-9]{2}-[0-9]{2}" dates.txt # date format
grep -E "[A-Z][a-z]+" names.txt # Capitalized words
# Quantifiers
grep -E "ab+c" file.txt # one or more b's
grep -E "ab*c" file.txt # zero or more b's
grep -E "ab?c" file.txt # zero or one b
# Alternation and grouping
grep -E "(cat|dog) food" file.txt
# PCRE (-P, GNU grep only)
grep -P "\d{3}-\d{4}" phones.txt # \d for digitsContexto do grep & Controle de Saída
Flags de contexto (-B, -A, -C) mostram linhas ao redor, essenciais para entender entradas de log. -o produz apenas a parte correspondida (útil para extrair URLs, números, etc.). --color destaca correspondências no terminal. -r busca recursivamente (exclui symlinks); -R segue symlinks. --include/--exclude/--exclude-dir filtram quais arquivos buscar — inestimável para codebases grandes (sempre exclua node_modules, .git, vendor). -a força arquivos binários a serem tratados como texto. Para busca de código, ripgrep (rg) é uma alternativa moderna e mais rápida com defaults sensatos.
# Show context: lines before/after match
grep -B 2 "error" log.txt # 2 lines Before
grep -A 3 "error" log.txt # 3 lines After
grep -C 2 "error" log.txt # 2 lines Context (both)
# Only show matched part (not full line)
grep -o "https?://[^ ]+" urls.txt
# Color highlight matches
grep --color=auto "error" log.txt
# Recursive search
grep -r "TODO" ./src/
grep -R "TODO" ./src/ # follows symlinks
# Include/exclude file patterns
grep -r --include="*.py" "import" .
grep -r --exclude="*.test.js" "TODO" .
grep -r --exclude-dir=node_modules "TODO" .
# Binary files
grep -a "pattern" binary_file # treat as textgrep com stdin & Pipelines
grep é mais poderoso em pipelines. O truque [n]ginx previne que o grep corresponda seu próprio processo: o colchete faz o padrão não corresponder à string literal 'grep' na lista de processos. zgrep busca arquivos comprimidos sem descompressão manual. pgrep é um localizador de processo especializado (melhor que ps | grep). grep -rl lista arquivos contendo um padrão; fazer pipe para xargs grep busca dentro desses arquivos por outro padrão — uma técnica comum de arqueologia de código. Para busca interativa de código, use ripgrep ou ack que são projetados para código-fonte.
# Search command output
ps aux | grep nginx
ps aux | grep "[n]ginx" # trick: prevents matching grep itself
# Search compressed logs
zcat log.gz | grep "error"
zgrep "error" log.gz # direct
# Chain multiple greps
cat log.txt | grep "error" | grep -v "timeout" | grep -c
# Extract and filter
grep -oE "[0-9.]+" response.txt | sort -n | uniq
# Find processes excluding grep
pgrep -f "node server.js" # better than ps | grep
# Search command history
history | grep "git rebase"
# Find files containing pattern, then search more
grep -rl "config" . | xargs grep "database"Exit Status do grep & Scripting
O exit status do grep o torna ideal para scripting: 0 (correspondência encontrada), 1 (sem correspondência), 2 (erro). -q (quiet) suprime saída para verificações condicionais puras. Em scripts com set -e, grep retornando 1 (sem correspondência) sairia do script — use '|| true' para evitar isso. O padrão while-read processa linhas correspondentes uma de cada vez. grep -c retorna uma contagem (0 se sem correspondências), que você pode comparar numericamente. Essa capacidade de scripting torna o grep um bloco de construção para monitoramento de logs, scripts de valida ção e verificações de CI/CD.
# grep returns exit codes for scripting
# 0 = match found, 1 = no match, 2 = error
if grep -q "error" /var/log/syslog; then
echo "Errors found!"
# send alert, etc.
fi
# Silent check (-q), no output
grep -q "^$" file.txt && echo "has blank lines"
# Use in while loop
grep "pattern" file.txt | while read -r line; do
echo "Found: $line"
done
# Count and branch
errors=$(grep -c "ERROR" log.txt)
if [ "$errors" -gt 10 ]; then
echo "Too many errors: $errors"
fi
# grep with set -e (exits on non-zero)
set -e
grep "missing" file.txt || true # prevent exitsed Aprofundado
Substituição com sed
sed (stream editor) transforma texto linha por linha. O comando s substitui texto: s/pattern/replacement/flags. g (global) substitui todas as ocorrências por linha; sem ele, apenas a primeira correspondência é substituída. Você pode restringir operações a linhas específicas (por número, intervalo ou padrão). -i edita arquivos in-place (perigoso — sempre teste sem -i primeiro, ou use -i.bak para um backup). sed processa cada linha independentemente. O delimitador não precisa ser / — use s|old|new|g quando padrões contêm barras (ex.: paths de arquivo).
# Basic substitution (first occurrence per line)
sed 's/old/new/' file.txt
# Replace all occurrences
sed 's/old/new/g' file.txt
# Case-insensitive (GNU sed)
sed 's/old/new/gi' file.txt
# Replace Nth occurrence
sed 's/old/new/2' file.txt # 2nd occurrence only
# Only on lines matching pattern
sed '/error/s/old/new/' file.txt
# Only on specific line numbers
sed '5s/old/new/' file.txt # line 5 only
sed '5,10s/old/new/g' file.txt # lines 5-10
sed '$s/old/new/' file.txt # last line
# In-place edit (modifies file)
sed -i 's/old/new/g' file.txt
sed -i.bak 's/old/new/g' file.txt # keeps backupsed com Regex & Grupos de Captura
sed -E (ou -r) usa Extended Regular Expressions com sintaxe mais limpa (sem backslash antes de parênteses/chaves). Grupos de captura são referenciados como \1, \2, etc. na substituição. & representa a correspondência completa. O exemplo de reformatação de data mostra o poder: capture ano, mês, dia separadamente e reorganize. Múltiplos comandos podem ser encadeados com ponto-e-vírgula (s/.../.../;s/.../.../). Sempre escape caracteres especiais em padrões (., *, [, etc.) e escape / em substituições (ou use um delimitador diferente).
# Extended regex (-E or -r)
sed -E 's/[0-9]+/NUMBER/g' file.txt
# Capture groups with \1, \2 (BRE)
sed 's/\(foo\)\(bar\)/\2\1/' file.txt # swap: foobar -> barfoo
# Capture groups with () (ERE, cleaner)
sed -E 's/(foo)(bar)/\2\1/' file.txt
# Reformat date: 2025-01-15 -> 15/01/2025
sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/' file.txt
# Add prefix to lines
sed 's/^/>> /' file.txt
# Add suffix
sed 's/$/ <<</' file.txt
# Remove leading/trailing whitespace
sed 's/^[ \t]*//;s/[ \t]*$//' file.txt
# Use & for full match
sed 's/[0-9]\{3\}/[&]/g' file.txt # wrap 3 digits in bracketssed Delete & Print
O comando d deleta linhas; p imprime linhas. Com -n (sem auto-print), apenas comandos p explícitos produzem saída — isso transforma sed em uma impressora seletiva (como grep). Deletar linhas em branco (sed '/^$/d') é uma limpeza comum. sed -n '5,10p' é equivalente a sed -n '5,10p' ou combinações head/tail. A sintaxe ~ (0~3p) imprime cada 3ª linha (extensão GNU). Lembre-se: sem -n, sed imprime cada linha (possivelmente modificada); com -n, não imprime nada a menos que você use p. Essa dualidade torna sed tanto um editor quanto um filtro.
# Delete lines
sed '5d' file.txt # delete line 5
sed '5,10d' file.txt # delete lines 5-10
sed '/pattern/d' file.txt # delete matching lines
sed '/^$/d' file.txt # delete blank lines
sed '/^[ \t]*$/d' file.txt # delete whitespace-only lines
sed '$d' file.txt # delete last line
# Print only specific lines (like head/tail)
sed -n '5p' file.txt # print line 5 only
sed -n '5,10p' file.txt # print lines 5-10
sed -n '/pattern/p' file.txt # print matching lines (like grep)
sed -n '5,${/pattern/p}' file.txt # from line 5 to end
# Print with line numbers
sed '=' file.txt | sed 'N;s/\n/\t/'
# Print every Nth line
sed -n '0~3p' file.txt # every 3rd line (GNU)sed Multi-linha & Hold Space
O hold space do sed permite operações multi-linha — o pattern space (linha atual) pode ser salvo no hold space e recuperado depois. h/H copia/anexa para hold; g/G copia/anexa de hold; x os troca. O truque de junção de linhas (:a;N;$!ba;s/\n/ /g) lê o arquivo inteiro no pattern space e depois substitui newlines. N anexa a próxima linha ao pattern space. Esses recursos avançados tornam sed Turing-completo mas também muito críptico. Para transformações multi-linha complexas, awk ou Perl são mais legíveis. Use sed para edições simples baseadas em linha; use awk para lógica baseada em campos ou multi-linha.
# Join lines (replace newline with space)
sed ':a;N;$!ba;s/\n/ /g' file.txt
# Join every 2 lines
sed 'N;s/\n/ /' file.txt
# Print paragraph (blocks separated by blank lines)
sed '/./{H;d;};x;s/\n/ /g' file.txt
# Reverse line order (tac alternative)
sed '1!G;h;$!d' file.txt
# Double-space a file
sed 'G' file.txt
# Remove last line of each paragraph
sed -n '/^$/{p;h;};/./{H;};/^$/{x;s/\n.*//;p;}' file.txt
# Hold space (h,H,g,G,x) stores lines between cycles
# h: copy pattern space to hold
# H: append pattern space to hold
# g: copy hold to pattern space
# G: append hold to pattern space
# x: exchange pattern and holdsed em Pipelines & Scripts
sed se destaca em pipelines para transformação de texto. Ao substituir variáveis, escape caracteres especiais (& e \) para evitar interpretação. Múltiplas flags -e aplicam múltiplos comandos em sequência. -f lê comandos de um arquivo (útil para scripts complexos). A conversão CSV-para-TSV mostra um uso prático. Para edição de arquivos de config em scripts, sempre valide a mudança (grep após sed) e considere usar ferramentas dedicadas (jq para JSON, yq para YAML). A força do sed é edições simples, rápidas e baseadas em linha — é um elemento básico do processamento de texto Unix ao lado do grep e awk.
# Clean up output in pipeline
cat messy.txt | sed 's/[ \t]*$//' | sed '/^$/d' | sort
# Extract and transform
echo "name=John;age=30" | sed 's/;/\n/g' | sed 's/=/: /'
# Modify config files in scripts
sed -i "s/PORT=8080/PORT=$NEW_PORT/" config.env
# Escape special chars for safe substitution
ESCAPED=$(printf '%s\n' "$URL" | sed 's/[&\]/\\&/g')
sed "s|URL|$ESCAPED|g" template.txt
# Multiple substitutions
sed -e 's/foo/bar/g' -e 's/baz/qux/g' file.txt
# Read commands from file
sed -f script.sed input.txt
# Convert CSV to TSV
sed 's/,/\t/g' data.csvawk Aprofundado
Básicos de awk & Campos
awk automaticamente divide cada linha em campos ($1, $2, ..., $NF). -F define o separador de campo de entrada; OFS define o separador de saída. $0 é a linha inteira. NR (Number of Records) é o número da linha; NF (Number of Fields) é a contagem de campos da linha atual. awk processa cada linha através de pares padrão-ação: pattern { action }. Se nenhum padrão, a ação roda para cada linha. Se nenhuma ação, a linha é impressa. Isso torna awk ideal para extração de dados baseada em colunas de CSV, TSV, /etc/passwd e arquivos de log.
# Print specific fields (default delimiter: whitespace)
awk '{print $1}' file.txt # first field
awk '{print $1, $3}' file.txt # first and third
awk '{print $NF}' file.txt # last field
awk '{print $(NF-1)}' file.txt # second-to-last
# Custom field separator
awk -F: '{print $1, $7}' /etc/passwd # colon-separated
awk -F',' '{print $2}' data.csv # CSV
awk -F'\t' '{print $1}' data.tsv # TSV
# Print entire line ($0)
awk '{print}' file.txt
awk '{print NR, $0}' file.txt # with line number
# NR = record (line) number
# NF = number of fields in current record
# NR and NF are built-in variables
# Change output separator
awk 'BEGIN{OFS="|"} {print $1, $2, $3}' file.txtPadrões & Condições do awk
Padrões awk podem ser regex (/pattern/), comparações ($3 > 100), números de linha (NR == 5), ou intervals (/start/,/end/). ~ e !~ aplicam regex a campos específicos. Condições podem ser combinadas com &&, ||, e !. Isso torna awk um filtro poderoso — mais expressivo que grep porque você pode comparar valores de campo numericamente ou como strings. O padrão de intervalo (/start/,/end/) imprime todas as linhas entre dois marcadores (inclusivo). awk avalia condições por linha; se true, a ação roda. Sem uma ação, o padrão é {print}.
# Pattern: only process matching lines
awk '/error/ {print}' log.txt # like grep
awk '$3 > 100' data.txt # 3rd field > 100
awk 'NR == 5' file.txt # line 5 only
awk 'NR >= 10 && NR <= 20' file.txt # lines 10-20
awk 'NF == 0' file.txt # empty lines
awk '$1 == "ERROR"' log.txt # first field equals ERROR
# Range pattern: from start to end
awk '/start/,/end/' file.txt
# Combine conditions
awk '$3 > 50 && $3 < 100 {print $1, $3}' data.txt
awk '$1 == "GET" || $1 == "POST" {print}' access.log
# Negation
awk '!/debug/ {print}' log.txt # lines NOT containing debug
# Field comparison with regex
awk '$2 ~ /^[0-9]+$/' file.txt # 2nd field is numeric
awk '$2 !~ /pattern/' file.txt # 2nd field doesn't matchBEGIN/END & Variáveis do awk
BEGIN roda antes de qualquer entrada ser lida (ideal para inicialização, headers, setup de variáveis). END roda após toda entrada ser processada (ideal para resumos, totais). Variáveis de usuário não precisam de declaração — elas defaultam para 0 (numérico) ou vazio (string). -v passa variáveis externas para o awk. Isso torna awk uma mini linguagem de programação para processamento de dados: soma, média, min, max, contagem — tudo em uma passagem. O padrão 'NR == 1 {max = $1}' inicializa max da primeira linha, depois linhas subsequentes o atualizam. Isso é muito mais eficiente que múltiplos pipelines grep/sort/cut.
# BEGIN: runs before processing (setup)
# END: runs after processing (summary)
awk 'BEGIN {print "Total:"} {sum += $1} END {print sum}' nums.txt
# Count lines
awk 'END {print NR " lines"}' file.txt
# Sum a column
awk '{sum += $2} END {print "Total:", sum}' sales.csv
# Average
awk '{sum += $2; count++} END {print sum/count}' data.txt
# Find max value
awk 'NR == 1 {max = $1} $1 > max {max = $1} END {print max}' nums.txt
# User-defined variables (no declaration needed)
awk '{total += $1; if ($1 > max) max = $1}
END {print "Sum:", total, "Max:", max, "Avg:", total/NR}'
# Pass variables with -v
awk -v threshold=50 '$1 > threshold {print}' data.txt
awk -v date="$(date +%Y-%m-%d)" '{print date, $0}' file.txtControle de Fluxo do awk
awk tem controle de fluxo completo: if/else, for, while, e arrays associativos (key-value). Isso o torna uma linguagem completa de processamento de dados. O exemplo de contagem de palavras (words[$1]++) é o caso de uso clássico do awk — contar ocorrências de cada valor em uma coluna, depois ordenar por frequência. Arrays no awk são associativos (como dicionários), indexados por string ou número. for (key in array) itera chaves (não ordenado — faça pipe através do sort). awk pode substituir pipelines inteiros de cut, sort, uniq e wc com uma única passagem eficiente. Para processamento complexo de dados, awk é frequentemente mais claro que comandos Unix encadeados.
# if-else
awk '{
if ($3 >= 90) grade = "A"
else if ($3 >= 80) grade = "B"
else if ($3 >= 70) grade = "C"
else grade = "F"
print $1, grade
}' students.txt
# for loop
awk 'BEGIN {
for (i = 1; i <= 5; i++) print i, i*i
}'
# for-in (iterate array)
awk '{count[$1]++} END {
for (key in count) print key, count[key]
}' access.log | sort -rnk2
# while loop
awk '{
i = 1
while (i <= NF) {
print i, $i
i++
}
}' file.txt
# Arrays (associative)
awk '{words[$1]++} END {
for (w in words) print words[w], w
}' file.txt | sort -rn | headExemplos Práticos de awk
Esses exemplos práticos mostram o poder do awk no mundo real. A contagem de frequência de IP é essencial para análise de logs. O resumo de tamanho de arquivo por extensão usa split() para extrair extensões. Filtragem de CSV por valor de coluna substitui pipelines complexos grep/sed. Cálculo de percentil demonstra as capacidades matemáticas do awk (asorti ordena índices de array). Reformatação de colunas (mudar delimitador e selecionar campos) é uma tarefa ETL comum. awk é a ferramenta go-to para processamento de texto estruturado — quando dados têm colunas/campos, awk é quase sempre a escolha certa. Para JSON, use jq; para CSV, awk ou csvkit.
# Top 10 IP addresses from access log
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head
# Or entirely in awk:
awk '{ip[$1]++} END {for (i in ip) print ip[i], i}' access.log | sort -rn | head
# Sum file sizes by extension
ls -l | awk '{split($NF, a, "."); ext=a[2]; size[ext]+=$5}
END {for (e in size) print e, size[e]}' | sort -rnk2
# Filter CSV by column value
awk -F',' '$3 == "active" {print $1, $2}' users.csv
# Calculate response time percentiles from log
awk '{times[NR] = $2} END {
n = asorti(times, sorted)
print "p50:", sorted[int(n*0.5)]
print "p95:", sorted[int(n*0.95)]
print "p99:", sorted[int(n*0.99)]
}' response_times.txt
# Reformat columns
awk -F',' 'BEGIN{OFS="|"} {print $1, $2, $4}' data.csvfind & xargs
find por Nome & Tipo
find busca no sistema de arquivos por vários critérios. -name corresponde ao nome do arquivo (case-sensitive); -iname é case-insensitive. -type f/d/l filtra por tipo de arquivo. -path corresponde ao path completo; -regex usa regex de path completo. -o (ou) combina condições; use \( \) para agrupar. -maxdepth limita a profundidade de recursão (importante para performance em sistemas de arquivos grandes). find produz paths; combine com -exec ou xargs para agir sobre os resultados. Sempre coloque padrões entre aspas para evitar expansão glob do shell. Para busca de código, ripgrep (rg) é mais rápido mas find é mais flexível para operações de sistema de arquivos.
# Find by name (exact)
find . -name "config.txt"
# Case-insensitive name
find . -iname "config.txt"
# Wildcard pattern
find . -name "*.py"
find . -name "test_*"
# Find by type
find . -type f -name "*.js" # files only
find . -type d -name "node_modules" # directories
find . -type l # symlinks
# Find by path (full path match)
find . -path "*/src/*.test.js"
# Find by regex
find . -regex ".*\.\(py\|js\)$"
# Multiple name patterns
find . \( -name "*.py" -o -name "*.js" \)
# Max depth (don't recurse too deep)
find . -maxdepth 2 -name "*.txt"find por Tempo & Tamanho
A busca baseada em tempo do find é essencial para limpeza e auditoria. -mtime (modificação), -atime (acesso), -ctime (mudança de metadados) usam dias; -mmin/-amin/-cmin usam minutos. - (menos que) e + (mais que) prefixam o valor. Tamanho usa sufixos: c (bytes), k (KB), M (MB), G (GB). -empty encontra arquivos de comprimento zero ou diretórios vazios. -perm verifica permissões: correspondência exata (644), todos os bits set (-u+x), ou qualquer bit set (/4000). Encontrar arquivos SUID (/4000) é uma técnica de auditoria de segurança. Esses critérios podem ser combinados com -a (e, padrão) e -o (ou).
# Find by modification time
find . -mtime -1 # modified in last 24 hours
find . -mtime +7 # modified more than 7 days ago
find . -mtime 7 # modified exactly 7 days ago
# Access time (-atime) and change time (-ctime)
find . -atime -1 # accessed in last 24h
find . -ctime -1 # status changed in last 24h
# Minutes instead of days
find . -mmin -30 # modified in last 30 minutes
# Find by size
find . -size +100M # larger than 100MB
find . -size -1k # smaller than 1KB
find . -size 10M # exactly 10MB
# Find empty files/directories
find . -empty -type f
find . -empty -type d
# Find by permissions
find . -perm 644 # exactly 644
find . -perm -u+x # has execute for user
find / -perm /4000 # SUID files (security audit)find -exec & -delete
-exec roda um comando em cada resultado. {} é o placeholder para o nome do arquivo; \; encerra o comando (roda uma vez por arquivo); + agrupa arquivos (roda uma vez com todos os arquivos — mais eficiente). -delete remove arquivos correspondidos (mais rápido que -exec rm, mas teste primeiro com -print). O padrão chmod (dirs 755, arquivos 644) é uma configuração comum de servidor web. -exec com + é preferido para comandos que aceitam múltiplos arquivos (grep, ls, wc). Para deleção, sempre rode com -print primeiro para verificar o que será deletado, depois substitua por -delete. -ok em vez de -exec pede confirmação por arquivo.
# Execute command on each result
find . -name "*.log" -exec ls -lh {} \;
# Execute with + (batch, more efficient)
find . -name "*.log" -exec ls -lh {} +
# Delete files (DANGEROUS - test first!)
find /tmp -name "*.tmp" -type f -delete
# Safer: use -exec rm
find /tmp -name "*.tmp" -type f -exec rm -i {} \;
# Execute a complex command
find . -name "*.py" -exec wc -l {} \; | sort -rn | head
# Change permissions
find . -type d -exec chmod 755 {} \; # dirs to 755
find . -type f -exec chmod 644 {} \; # files to 644
# Find and grep
find . -name "*.py" -exec grep "TODO" {} +
# Print before deleting (confirmation)
find . -name "*.bak" -print -deleteBásicos do xargs
xargs converte stdin em argumentos de comando. É a ponte entre a saída do find e comandos que não leem stdin. -n limita argumentos por comando; -I {} define um placeholder para posicionamento customizado. -0 (com find -print0) lida com nomes de arquivo com espaços/newlines corretamente — sempre use esse par para segurança. -P ativa execução paralela (ótimo para tarefas CPU-bound como conversão de imagens). -t (trace) mostra comandos antes de rodar; -p pede confirmação. xargs é essencial quando -exec é muito lento (um processo por arquivo) — xargs agrupa argumentos eficientemente.
# Basic: take stdin and pass as arguments
echo "file1 file2 file3" | xargs rm
# One argument per line
find . -name "*.bak" | xargs rm
# Control arguments per command (-n)
echo "1 2 3 4 5" | xargs -n 2 echo # echo 1 2; echo 3 4; echo 5
# Show commands before running (-t, verbose)
find . -name "*.log" | xargs -t rm
# Prompt before each command (-p)
find . -name "*.tmp" | xargs -p rm
# Handle filenames with spaces (-0 with find -print0)
find . -name "*.txt" -print0 | xargs -0 grep "pattern"
# Replace string (-I)
find . -name "*.py" | xargs -I {} cp {} /backup/
# Parallel execution (-P)
find . -name "*.png" | xargs -P 4 -I {} convert {} {}.thumb.png
# Limit with -L (lines per command)
cat urls.txt | xargs -L 1 curl -OPadrões find + xargs
find + xargs é o padrão Unix clássico para operações em lote de arquivos. -print0 | xargs -0 é a combinação segura para nomes de arquivo com espaços ou caracteres especiais. O padrão de grep em massa é muito mais rápido que -exec grep (um processo grep vs muitos). xargs paralelo (-P N) acelera dramaticamente tarefas CPU-bound como conversão de áudio/vídeo. O padrão de archive (encontrar logs antigos, tar them) é uma técnica comum de rotação de logs. Sempre use -print0/-0 para robustez — sem isso, nomes de arquivo com espaços, aspas ou newlines quebrarão o pipeline. Essa combinação é fundamental para administração de sistemas Unix.
# Find and bulk grep (efficient)
find . -name "*.py" -print0 | xargs -0 grep -l "import django"
# Find large files and show sizes
find . -type f -size +100M -print0 | xargs -0 ls -lhS
# Find and archive
find . -name "*.log" -mtime +30 -print0 |
xargs -0 tar -czf old_logs.tar.gz
# Find and batch rename
find . -name "*.JPG" | xargs -I {} mv {} {}.bak
# Or with rename command:
find . -name "*.JPG" -exec rename 's/\.JPG$/.jpg/' {} +
# Find empty directories and remove
find . -type d -empty -print0 | xargs -0 rmdir
# Find recently modified files and copy
find . -name "*.py" -mtime -1 -print0 |
xargs -0 -I {} cp {} /tmp/recent/
# Parallel processing
find . -name "*.wav" -print0 |
xargs -0 -P 8 -I {} ffmpeg -i {} {}.mp3Técnicas Avançadas de Bash
Here Document & Here String
Here documents (<< DELIMITER) alimentam texto multi-linha como stdin para um comando — útil para gerar arquivos de config, queries SQL, ou qualquer entrada multi-linha. Delimitador sem aspas permite expansão de variável; com aspas ('EOF') trata o conteúdo literalmente. Here strings (<<<) alimentam uma única string como stdin — mais limpo que echo | command para casos simples. O delimitador pode ser qualquer palavra (EOF, END, DONE); a convenção é maiúsculas. Here docs são essenciais para scripts que geram arquivos ou interagem com programas interativos (mysql, psql, ssh). Delimitadores indentados (<<-) removem tabs iniciais para legibilidade.
# Here document: multi-line input to a command
cat << EOF
Line 1
Line 2 with $VARIABLE expansion
Line 3
EOF
# Quoted delimiter: no variable expansion
cat << 'EOF'
This $won't expand
\n stays literal
EOF
# Write to file
cat > config.txt << EOF
host=localhost
port=8080
EOF
# Here string: single string as stdin
grep "pattern" <<< "search this text"
read -r line <<< "hello world"
# With variables
name="Alice"
greeting=$(cat <<< "Hello, $name!")
# Pipe multi-line to command
mysql << EOF
USE mydb;
SELECT * FROM users;
EOFExpansão de Parâmetro
Expansão de parâmetro é a manipulação de string integrada do Bash — sem necessidade de sed/awk/cut para operações simples. :- fornece defaults; # e ## removem prefixos; % e %% removem sufixos. Os exemplos de extensão de arquivo são extremamente comuns: ${file##*.} obtém a extensão, ${file%.*} obtém o basename sem extensão. / substitui a primeira correspondência; // substitui todas. ^^ e ,, convertem case (Bash 4+). Essas operações são mais rápidas que spawnar comandos externos. Domine a expansão de parâmetro para escrever scripts Bash eficientes e legíveis sem subshells desnecessários.
# Default values
echo ${name:-"default"} # use "default" if name unset/empty
echo ${name:="default"} # set name to "default" if unset/empty
# String length
echo ${#var}
# Substring extraction
var="Hello World"
echo ${var:0:5} # Hello (start:length)
echo ${var:6} # World (start to end)
# Remove from beginning (# shortest, ## longest)
file="archive.tar.gz"
echo ${file#*.} # tar.gz (remove up to first .)
echo ${file##*.} # gz (remove up to last .)
# Remove from end (% shortest, %% longest)
echo ${file%.*} # archive.tar (remove from last .)
echo ${file%%.*} # archive (remove from first .)
# Replace
echo ${var/World/Bash} # Hello Bash (first match)
echo ${var//l/L} # HeLLo WorLd (all matches)
# Case conversion (Bash 4+)
echo ${var^^} # HELLO WORLD (uppercase)
echo ${var,,} # hello world (lowercase)trap & Tratamento de Sinais
trap registra handlers para sinais e eventos. EXIT dispara no término do script (normal, erro, ou morto) — ideal para limpeza (arquivos temporários, locks). INT (Ctrl-C), TERM (kill), HUP (terminal fechado) são sinais comuns. O padrão de arquivo temporário (trap 'rm -f' EXIT) garante limpeza mesmo se o script falhar. DEBUG dispara antes de cada comando — útil para rastreamento. Sempre coloque o comando do trap entre aspas (aspas simples previnem expansão imediata). trap é essencial para scripts robustos: sem ele, arquivos temporários se acumulam e locks podem não ser liberados em caso de falha. Sempre limpe após você mesmo.
# Clean up on exit (any reason)
cleanup() {
echo "Cleaning up..."
rm -f /tmp/myapp.lock
exit 0
}
trap cleanup EXIT
# Handle specific signals
trap 'echo "Interrupted!"; cleanup' INT TERM
trap 'echo "Ctrl-C pressed"' INT
# Ignore a signal
trap '' INT # ignore Ctrl-C
# Reset trap
trap - INT # restore default behavior
# Common pattern: temp file cleanup
TMPFILE=$(mktemp)
trap 'rm -f "$TMPFILE"' EXIT
echo "data" > "$TMPFILE"
# ... use TMPFILE ...
# File is removed when script exits (even on error)
# Debug trap (trace execution)
trap 'echo "DEBUG: $BASH_COMMAND"' DEBUGDebugando Scripts Bash
set -x rastreia a execução (imprime cada comando com prefixo +) — a principal ferramenta de debugging. set -euo pipefail (strict mode) captura erros cedo: -e sai em non-zero, -u captura erros de digitação em nomes de variável, pipefail faz pipes falharem corretamente (sem ele, apenas o exit code do último comando importa). bash -n verifica sintaxe sem rodar. PS4 customiza o prefixo de trace (mostrar file:line é muito útil). trap ERR dispara em qualquer erro (com -e), dando contexto de número de linha. Para scripts complexos, adicione set -x em seções suspeitas em vez de globalmente. Sempre use strict mode em scripts de produção.
# Trace execution: print each command before running
bash -x script.sh
# Or within script:
set -x # enable tracing
set +x # disable tracing
# Verbose: print input lines as read
bash -v script.sh
# Strict mode (recommended for all scripts)
set -euo pipefail
# -e: exit on error
# -u: error on undefined variable
# -o pipefail: pipe fails if any command fails
# -o pipefail: pipe fails if any command fails
# Print specific debug info
echo "DEBUG: var=$var" >&2 # to stderr
# Check syntax without executing
bash -n script.sh
# PS4: customize trace prompt
export PS4='+${BASH_SOURCE}:${LINENO}: '
bash -x script.sh
# Trap ERR for error handling
trap 'echo "Error on line $LINENO"' ERRcurl & wget
curl e wget são clientes HTTP essenciais. curl é para teste de API (suporta todos os métodos HTTP, headers, JSON). -d envia dados POST; -H define headers; -X especifica método; -L segue redirecionamentos; -O salva com nome de arquivo remoto; -s é silencioso (para scripting). wget é otimizado para download (resume com -c, recursivo com -r). Para teste de REST API, curl com -H 'Content-Type: application/json' e -d para body JSON é o padrão. -w '%{http_code}' extrai apenas o status code para scripting. Para teste complexo de API, considere httpie (sintaxe mais simples) ou postman.
# Basic HTTP request
curl https://api.example.com/data
curl -O https://example.com/file.zip # save to file
# HTTP methods
curl -X POST https://api.example.com/users
curl -X PUT -d '{"name":"Alice"}' https://api.example.com/users/1
curl -X DELETE https://api.example.com/users/1
# Headers and JSON
curl -H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-d '{"key":"value"}' \
https://api.example.com/data
# Download with wget
wget https://example.com/file.zip
wget -q https://example.com/file.zip # quiet
wget -c https://example.com/file.zip # continue/resume
# Follow redirects
curl -L https://short.url/abc
# Save response, show headers
curl -s -o response.json -w "%{http_code}" https://api.example.com
# Download with progress bar
curl --progress-bar -O https://example.com/largefile.iso
# POST form data
curl -d "name=Alice&[email protected]" https://api.example.com/formSinais & Traps
handlers de limpeza com trap
trap registra handlers para sinais. EXIT é especial — dispara na saída do shell por qualquer motivo, tornando-o perfeito para limpeza. Sempre limpe arquivos de lock, dirs temporários e processos filhos em um trap. Sinais comuns para trap: INT (Ctrl-C), TERM (kill padrão), HUP (terminal fechado), EXIT.
cleanup() {
echo "Cleaning up..."
rm -f /tmp/myapp.lock
exit 0
}
# catch INT (Ctrl-C), TERM, EXIT
trap cleanup INT TERM EXIT
# normal exit also triggers EXIT trap
echo "Working..."
sleep 30
# cleanup runs automatically
# remove trap
trap - EXIT # disable
trap - INT TERM EXIT # disable allarquivos temporários com limpeza
Nunca crie nomes de arquivo temporário manualmente com $$ ou $RANDOM — paths previsíveis convidam ataques de symlink. mktemp cria um arquivo/dir com nome único atomicamente. Combine com um trap em EXIT para garantir limpeza mesmo em erro ou interrupção. Defina a variável de ambiente TMPDIR para controlar a localização.
TMPDIR=$(mktemp -d)
TMPFILE=$(mktemp)
cleanup() {
rm -rf "$TMPDIR" "$TMPFILE"
}
trap cleanup EXIT
# use temp files safely
echo "data" > "$TMPFILE"
process_data < "$TMPFILE"
# mktemp is safer than $RANDOM — avoids symlink attacks
# -d creates a directory
# -p /var/tmp specifies parent
# mktemp -t myapp.XXXXXX # use TMPDIR envtratamento de sinais em scripts
Para scripts de longa execução, use uma variável flag invertida pelo handler de sinal para sair do loop principal graciosamente. Isso permite terminar a iteração atual, persistir estado e sair de forma limpa. Evite fazer trabalho pesado dentro do próprio handler do trap — apenas defina flags.
#!/bin/bash
# graceful shutdown for a long-running script
RUNNING=1
stop() {
echo "Stopping..."
RUNNING=0
}
trap stop INT TERM
while [ $RUNNING -eq 1 ]; do
echo "Tick $(date)"
sleep 1
done
echo "Done."
# send signals: kill -TERM <pid> or Ctrl-Ctimeout e watch
timeout roda um comando e o mata (SIGTERM por padrão) após uma duração. Exit code 124 significa que deu timeout. Use --kill-after para escalar para SIGKILL se o processo ignorar SIGTERM. Combine com loops para construir health checks. watch é para exibição periódica voltada para humanos, não scripts.
# timeout kills a command after N seconds
timeout 30 slow_command
timeout 5s curl https://slow.example.com
timeout --signal=KILL 60 build.sh # escalate to SIGKILL
timeout -k 5 30 command # kill -9 after 5s grace
# exit status: 124 if timed out
timeout 2 sleep 10
echo $? # 124
# watch reruns a command
watch -n 1 date
watch -n 5 'kubectl get pods'
# tlimit-style loops
while true; do
timeout 5 curl -s https://api.example.com/health || echo "down"
sleep 60
doneignorar e re-enviar
trap '' SIGNAL ignora um sinal (útil para seções críticas não-interrompíveis). Para encaminhar sinais a um filho, capture seu PID com $! e re-envie. O trap ERR dispara em qualquer falha de comando quando set -e está ativado — útil para registrar o número da linha que falhou via $LINENO.
# ignore a signal entirely
trap '' INT # ignore Ctrl-C
trap - INT # restore default
# re-raise / propagate to children
trap 'kill -TERM $childpid; wait $childpid' TERM
# forward signals to a subprocess
childpid=
run_child() {
some_server &
childpid=$!
wait $childpid
}
trap 'kill -TERM $childpid' TERM INT
run_child
# exit on any error
set -e
trap 'echo "Failed at line $LINENO"' ERRFunções & Bibliotecas
definindo funções
Funções agrupam comandos reutilizáveis. Duas sintaxes: name() {...} e function name {...}. Argumentos dentro de funções são $1, $2, etc. — $0 ainda é o nome do script. $@ expande para todos os args (sempre coloque entre aspas como "$@" para preservar args com espaços). $# é a contagem.
# two equivalent syntaxes
greet() {
echo "Hello, $1!"
}
function greet { # also valid
echo "Hello, $1!"
}
greet Alice # Hello, Alice!
greet "Bob Smith" # quoted args
# arguments: $1, $2, ... $@, $#, $*
echo_args() {
echo "Count: $#"
echo "All: $@"
echo "First: $1"
for arg in "$@"; do echo "- $arg"; done
}valores de retorno e locals
return define o exit status (0=success, 1-255=failure) — NÃO retorna um valor. Para obter um valor de volta, faça echo dele e capture com $(). Use local para variáveis dentro de funções para evitar poluir o escopo global. local é essencial em funções recursivas.
# return is exit status (0-255), not a value
is_even() {
if (( $1 % 2 == 0 )); then return 0; else return 1; fi
}
if is_even 4; then echo "even"; fi
# capture stdout as a value
get_date() { date +%Y-%m-%d; }
today=$(get_date)
# local variables
counter() {
local count=0 # scoped to function
((count++))
echo $count
}sourcing bibliotecas
source (ou .) executa um arquivo no shell atual — então funções, variáveis e aliases ficam disponíveis. É assim que você constrói bibliotecas reutilizáveis. BASH_SOURCE[0] vs $0 distingue sourced vs executado — útil para arquivos que são tanto uma biblioteca quanto um script executável.
# library file: lib/utils.sh
log() {
echo "[$(date +%H:%M:%S)] $*" >&2
}
CONFIG_PATH="/etc/myapp"
# main script
source lib/utils.sh # or: . lib/utils.sh
log "Starting..."
echo "Config: $CONFIG_PATH"
# check if sourced vs executed
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
echo "Running directly"
else
echo "Sourced"
fifunções recursivas
Bash suporta recursão, mas é lento (cada chamada spawn subshells para $()) e tem profundidade de pilha limitada (~1000s). Para trabalho computacional pesado, prefira awk, Python ou ferramentas externas. Sempre use variáveis locais em funções recursivas — caso contrário elas se sobrescrevem entre chamadas.
# factorial
factorial() {
local n=$1
if (( n <= 1 )); then
echo 1
else
local prev=$(factorial $((n - 1)))
echo $((n * prev))
fi
}
factorial 5 # 120
# fibonacci
fib() {
if (( $1 < 2 )); then echo $1; return; fi
echo $(( $(fib $(($1 - 1))) + $(fib $(($1 - 2))) ))
}
# caution: bash recursion is slow and limited depth
# for heavy work, use awk, python, or external toolsargumentos default e opcionais
${1:-default} substitui um default se $1 estiver unset ou vazio. ${1:?message} sai com erro se faltar — ótimo para args obrigatórios. shift remove $1 da lista de argumentos. Combinar case + shift é a maneira idiomática de fazer parse de flags e args posicionais em funções bash.
# default values
greet() {
local name="${1:-World}" # default if unset or empty
local greeting="${2:-Hello}"
echo "$greeting, $name!"
}
greet # Hello, World!
greet Alice # Hello, Alice!
greet Alice Hi # Hi, Alice!
# required argument
require_arg() {
: "${1:?Usage: require_arg <name>}" # exits with message if missing
echo "Got: $1"
}
# shift through args
process() {
while (( $# )); do
case "$1" in
-v) verbose=1; shift ;;
*) files+=("$1"); shift ;;
esac
done
}Arrays & Arrays Associativos
arrays indexados
Arrays indexados usam chaves inteiras baseadas em 0. Sempre coloque "${arr[@]}" entre aspas ao iterar para lidar com elementos com espaços corretamente. ${#arr[@]} é a contagem. ${!arr[@]} dá os índices (útil para arrays esparsos). unset 'arr[i]' (entre aspas) remove um elemento.
# declare and assign
fruits=("apple" "banana" "cherry")
fruits+=("date") # append
fruits[10]="kiwi" # sparse
# access
echo "${fruits[0]}" # first
echo "${fruits[@]}" # all values
echo "${#fruits[@]}" # count
echo "${!fruits[@]}" # indices
# iterate
for f in "${fruits[@]}"; do
echo "$f"
done
# slice
echo "${fruits[@]:1:2}" # 2 items starting at index 1
# delete
unset 'fruits[1]'arrays associativos (maps)
Arrays associativos (declare -A) são um recurso do bash 4+ (macOS vem com bash 3 por padrão — use brew install bash). As chaves são strings. O teste -v verifica se uma chave existe. Itere chaves com ${!arr[@]}. O /bin/bash padrão do macOS é 3.2 — scripts usando -A precisam de #!/usr/bin/env bash apontando para um bash mais novo.
# requires bash 4+
declare -A ages
ages["alice"]=30
ages["bob"]=25
ages["carol"]=42
# access
echo "${ages["alice"]}"
echo "${ages[@]}" # all values
echo "${!ages[@]}" # all keys
# iterate keys
for name in "${!ages[@]}"; do
echo "$name => ${ages[$name]}"
done
# check key exists
if [[ -v ages["alice"] ]]; then echo "yes"; fi
# count
echo "${#ages[@]}"
# delete
unset 'ages[bob]'lendo em arrays
mapfile (também conhecido como readarray) é a maneira mais rápida de sugar linhas em um array — sempre use -t para remover newlines finais. O loop while-read é portável mas mais lento. Para dividir uma string delimitada, defina IFS e use read -ra. A substituição de processo < <(cmd) evita problemas de escopo de variável de subshell.
# read lines into array
mapfile -t lines < file.txt # bash 4+
echo "${lines[0]}"
# alternative (portable)
lines=()
while IFS= read -r line; do
lines+=("$line")
done < file.txt
# split string into array
IFS=',' read -ra csv <<< "a,b,c,d"
echo "${csv[2]}" # c
# from command
mapfile -t files < <(find . -name "*.js")
# process each
for f in "${files[@]}"; do
echo "Processing $f"
doneoperações de array
Arrays Bash não têm muitas operações integradas (sem .indexOf, .reverse, .unique). Para essas, escreva loops ou faça pipe através de sort/uniq. O truque de array único usa um array associativo como um set. Para manipulação complexa de arrays, considere awk ou uma linguagem de programação real — arrays bash são melhores para listas simples.
# append
arr=(a b c)
arr+=(d e) # (a b c d e)
# length
echo "${#arr[@]}"
# slice
echo "${arr[@]:1:2}" # (b c)
# find index (no built-in; loop)
idx=-1
for i in "${!arr[@]}"; do
if [[ "${arr[$i]}" == "b" ]]; then idx=$i; break; fi
done
# reverse
n=${#arr[@]}
for ((i=n-1; i>=0; i--)); do
echo "${arr[$i]}"
done
# unique (preserve order)
declare -A seen
unique=()
for x in "${arr[@]}"; do
[[ -v seen[$x] ]] && continue
seen[$x]=1
unique+=("$x")
donedados aninhados com arrays
Bash não tem arrays aninhados nativos. Workarounds: (1) armazene records como strings unidas por delimitador e divida com IFS, (2) use um array associativo com chaves compostas como "r,c", (3) use indirection de variável com ${!name}. Para dados aninhados reais, mude para jq + JSON ou uma linguagem real.
# array of "records" (delimiter-based)
users=("alice:30:admin" "bob:25:user")
for u in "${users[@]}"; do
IFS=':' read -r name age role <<< "$u"
echo "$name ($age) - $role"
done
# simulate 2D with name-mangled vars
declare -A grid
grid["0,0"]=1; grid["0,1"]=2
grid["1,0"]=3; grid["1,1"]=4
for key in "${!grid[@]}"; do
IFS=',' read -r r c <<< "$key"
echo "[$r,$c] = ${grid[$key]}"
done
# arrays of arrays (not directly supported)
# workaround: use indirection
rows=("row0" "row1")
row0=(a b c)
row1=(d e f)
ref="${rows[0]}[@]"
echo "${!ref}" # a b cManipulação de Strings
expansão de parâmetro
Expansão de parâmetro é a ferramenta de string mais poderosa do bash. # e ## removem do início (correspondência mais curta/mais longa); % e %% removem do final. / substitui a primeira, // substitui todas. Isso evita spawnar subshells — muito mais rápido que fazer pipe através de sed para operações simples.
path="/var/log/app.log"
# length
echo "${#path}" # 16
# substring (offset:length)
echo "${path:5}" # log/app.log
echo "${path:5:3}" # log
# remove from front (shortest/longest)
echo "${path#*/}" # var/log/app.log
echo "${path##*/}" # app.log (basename)
# remove from end
echo "${path%/*}" # /var/log (dirname)
echo "${path%%.*}" # /var/log/app
# replace
echo "${path/log/LOG}" # /var/LOG/app.log (first)
echo "${path//log/LOG}" # all occurrencesconversão de case
Bash 4+ adicionou ,, (lower), ^^ (upper), ^ (capitaliza primeiro char), , (lower primeiro char). declare -u/-l auto-converte atribuições. Para bash mais antigo (padrão do macOS), use tr ou awk. Conversão de case é comum ao normalizar entrada do usuário ou construir identificadores.
s="Hello World"
# bash 4+
echo "${s,,}" # hello world (lower)
echo "${s^^}" # HELLO WORLD (upper)
echo "${s^}" # Hello World (capitalize first)
echo "${s,}" # hello World (lowercase first)
# pattern-based
echo "${s^^[aeiou]}" # hEllO wOrld (vowels upper)
# portable (pre-bash 4)
echo "$s" | tr 'a-z' 'A-Z'
echo "$s" | awk '{print toupper($0)}'
# declare typed variables
declare -u UPPER="hello" # HELLO
declare -l LOWER="WORLD" # worlddividindo e juntando
Dividir: defina IFS e use read -ra. Juntar: ${arr[*]} com IFS set funciona mas é delicado — a função join_by é mais confiável. O truque printf '%s' "$d%s" anexa o delimitador a cada arg restante, produzindo um join limpo. tr pode converter entre delimitadores e newlines.
# split on delimiter
csv="a,b,c,d"
IFS=',' read -ra parts <<< "$csv"
echo "${parts[2]}" # c
for p in "${parts[@]}"; do echo "$p"; done
# join with delimiter (bash 4+)
arr=(a b c d)
joined=$(IFS=,; echo "${arr[*]}")
echo "$joined" # a,b,c,d
# custom joiner
join_by() {
local d=$1; shift
local first=$1; shift
printf '%s' "$first"
printf '%s' "$d%s" "$@"
}
join_by " | " a b c # a | b | c
# split into lines
tr ',' '\n' <<< "a,b,c"busca e teste
Correspondência de padrões: == com wildcards (* ? [..]) faz correspondência glob. =~ faz regex estendido; grupos de captura vão para BASH_REMATCH. shopt -s nocasematch torna comparações [[ ]] case-insensitive. expr é legado — prefira [[ ]] para código novo.
s="Hello World"
# substring search
if [[ "$s" == *"World"* ]]; then echo "contains"; fi
if [[ "$s" == Hello* ]]; then echo "starts with"; fi
if [[ "$s" == *World ]]; then echo "ends with"; fi
# regex match
if [[ "$s" =~ ^H[a-z]+\sW[a-z]+$ ]]; then echo "match"; fi
echo "${BASH_REMATCH[0]}" # whole match
echo "${BASH_REMATCH[1]}" # first group
# case-insensitive
shopt -s nocasematch
[[ "$s" == *world* ]] && echo "yes"
shopt -u nocasematch
# index (no built-in; use expr)
expr "$s" : ".*World" # length up to matchformatação printf
printf é mais poderoso que echo — suporta especificadores de formato estilo C (%s, %d, %f, %x), largura/precisão e alinhamento. %()T formata tempo sem spawnar o comando date. printf -v armazena o resultado em uma variável. printf '=%.0s' {1..40} é um truque útil para repetir um caractere N vezes.
# basic
printf '%s\n' "hello" "world" # one per line
printf '%s = %d\n' "count" 42
# width and padding
printf '%10s\n' "right" # right-aligned, width 10
printf '%-10s|\n' "left" # left-aligned
printf '%05d\n' 42 # 00042 (zero-padded)
printf '%.2f\n' 3.14159 # 3.14
# date formatting
printf 'Today: %(%Y-%m-%d)T\n' -1
# to variable
printf -v today '%(%Y%m%d)T' -1
echo "$today" # 20260621
# repeat
printf '=%.0s' {1..40}; echo # 40 equals signsExpressões Regulares
regex do bash com =~
=~ usa ERE (regex estendido). Grupos de captura populam BASH_REMATCH (índice 0 = correspondência inteira, 1+ = grupos). Coloque a string entre aspas mas NÃO o regex (colocar o regex entre aspas o torna literal). Para correspondência case-insensitive, use shopt -s nocasematch. Regex é POSIX ERE — sem \d, \w; use [0-9], [A-Za-z0-9_].
s="user_42"
if [[ "$s" =~ ^([a-z]+)_([0-9]+)$ ]]; then
echo "Match!"
echo "Name: ${BASH_REMATCH[1]}" # user
echo "ID: ${BASH_REMATCH[2]}" # 42
fi
# email-ish check
email="[email protected]"
if [[ "$email" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then
echo "valid"
fi
# case-insensitive
shopt -s nocasematch
[[ "$s" =~ USER ]] && echo "yes"
shopt -u nocasematchpadrões de regex do grep
grep por padrão usa BRE (regex básico) onde +, ?, |, () precisam de backslashes. -E ativa ERE (sintaxe mais limpa). -P ativa PCRE com \d, \w, \b, lookarounds — mais poderoso mas menos portável (apenas GNU grep). Para scripts que devem rodar em macOS/BSD, prefira -E.
# basic (BRE) — default
grep 'foo' file
grep 'a\.' file # literal dot (escape in BRE)
grep 'a*b' file # zero or more a
# extended (ERE) — -E
grep -E 'a+b' file # one or more
grep -E 'a|b' file # alternation
grep -E '(ab)+' file # grouping
grep -E '[0-9]{3}' file # quantifier
# perl (PCRE) — -P
grep -P '\d{3}' file # digits
grep -P '\bword\b' file # word boundary
grep -P '(?<=foo)bar' file # lookbehind
# anchors
grep -E '^Error' file # line start
grep -E 'done$' file # line end
grep -E '^Error.*timeout$' filesubstituição com regex do sed
sed usa BRE por padrão (grupos de captura precisam \( \), backrefs \1). -E muda para ERE (() e +, ?, | mais limpos). A flag I torna a substituição case-insensitive (GNU sed). Use um delimitador diferente (s|...|...|) quando o padrão contém barras. [[:space:]] é portável para whitespace.
# BRE (default)
sed 's/foo/bar/' file
sed 's/a\.b/x/' file # literal dot
sed 's/a\(b\)c/\1/' file # capture with backrefs
# ERE with -E
sed -E 's/a+b/x/' file
sed -E 's/(foo|bar)/X/g' file
sed -E 's/([0-9]{4})/Year: \1/' file
# case-insensitive
sed 's/foo/bar/I' file
sed 's/[fF][oO][oO]/bar/g' file
# common recipes
sed -E 's/^[[:space:]]+//' file # trim leading whitespace
sed -E 's/[[:space:]]+$//' file # trim trailing
sed -E 's/ +/ /g' file # collapse spaces
sed -E 's#https?://[^ ]+##g' file # strip URLsregex do awk
awk usa ERE. O operador ~ testa uma string contra um regex. /pat1/, /pat2/ é um padrão de intervalo (corresponde de pat1 a pat2 inclusivo). gsub faz substituição global in-place. match() do gawk com um terceiro arg captura grupos em um array — útil mas não portável para mawk/POSIX awk.
# match operator ~
awk '$1 ~ /^[0-9]+/' file # field 1 is numeric
awk '$0 !~ /debug/' file # line doesn't contain
awk '/^Error/,/^$/' file # from Error to blank line
# case-insensitive
awk 'BEGIN { IGNORECASE=1 } /error/' file
awk 'tolower($0) ~ /error/' file
# gsub (global substitute)
awk '{ gsub(/foo/, "bar"); print }' file
awk '{ gsub(/[0-9]+/, "N"); print }' file
# match with capture (gawk)
awk 'match($0, /id=([0-9]+)/, m) { print m[1] }' file
# split on regex
awk '{ n = split($0, parts, /[,;\t]/); print n }' filereceitas comuns de regex
Essas receitas cobrem tarefas comuns de extração de texto. -o imprime apenas a parte correspondida. Note que regex não é ideal para parsear HTML, JSON ou XML — use parsers apropriados (jq para JSON, xmllint para XML). Para IPv4, esse regex corresponde ao formato mas não a ranges válidos (255.255.255.255 vs 999.999.999.999).
# IPv4
grep -E '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log
# URL
grep -Eo 'https?://[^[:space:]]+' file
# email
grep -Eo '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}' file
# date YYYY-MM-DD
grep -E '[0-9]{4}-[0-9]{2}-[0-9]{2}' file
# hex color
grep -Eo '#[0-9A-Fa-f]{6}' file
# strong password (8+, upper, lower, digit)
grep -E '^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).{8,}$' file
# trim whitespace
sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//' file
# extract domain from URL
sed -E 's#https?://([^/]+).*#\1#' fileDebugging
opções do set
set -e sai em qualquer falha de comando (must-have para scripts). set -u captura erros de digitação em nomes de variável. set -o pipefail faz um pipe falhar se qualquer parte falhar (sem ele, apenas o exit code do último comando importa). set -x rastreia a execução. Combinado como -euo pipefail, isso é 'strict mode' — o default mais seguro.
#!/bin/bash
set -e # exit on error
set -u # error on unset variable
set -o pipefail # pipe fails if any command fails
set -x # print commands (trace)
# or combined:
set -euo pipefail
# shorthand
set -Eeuxo pipefail # -E: ERR trap inherits
# strict mode for robust scripts
set -euo pipefail
IFS=$'\n\t'
# trace specific section
set -x
complex_logic
set +x
# debug a script
bash -x script.sh
bash -xv script.sh # verbose + tracerastreando com -x
set -x imprime cada comando antes da execução, com + como prefixo padrão (PS4). Customizar PS4 para incluir file:line:function torna traces muito mais úteis para debugging. BASH_XTRACEFD redireciona o trace para um FD diferente, para que você possa separar a saída de trace do stdout/stderr.
# trace entire script
bash -x script.sh
# trace inside script
set -x
echo "tracing on"
set +x
# customize PS4 (the trace prompt)
export PS4='+ ${BASH_SOURCE:-shell}:${LINENO}: ${FUNCNAME[0]:-main}() '
set -x
my_function
set +x
# trace to file
exec 5> /tmp/trace.log
BASH_XTRACEFD=5
set -x
# ... commands ...
set +x
exec 5>&-tratamento de erros e trap ERR
O trap ERR dispara quando um comando falha (com set -e). $? dentro do trap dá o exit code da falha. $LINENO mostra onde a falha ocorreu. O builtin caller imprime file:line:function — iterá-lo produz um stack trace. set -E faz o trap ERR disparar dentro de funções também (caso contrário é desativado em contexto de função).
set -e
set -E # ERR trap inherits functions
on_error() {
local exit_code=$?
local line=$1
echo "ERROR: command failed at line $line with exit code $exit_code" >&2
echo "Stack:" >&2
local i=0
while caller $i >&2; do ((i++)); done
exit $exit_code
}
trap 'on_error $LINENO' ERR
# now any failing command triggers the handler
false # triggers on_error
# caller builtin gives file:line:func
# stack trace via: while caller $i; do ((i++)); donelinter shellcheck
shellcheck é o linter de fato para scripts shell — captura problemas de quoting, vars não utilizadas, pegadinhas comuns e sugere idioms. Rode-o em cada script. A correção mais comum é SC2086: coloque variáveis entre aspas para evitar word-splitting e globbing. Muitos pipelines de CI exigem que o shellcheck passe.
# install: apt install shellcheck (or brew, or use shellcheck.net)
# run:
shellcheck script.sh
shellcheck -x script.sh # trust includes (no SC1090 warnings)
shellcheck -S warning script.sh # only warnings+
shellcheck --exclude=SC2086 *.sh # exclude specific rules
# common warnings:
# SC2086: double-quote to prevent globbing (use "$var")
# SC2046: quote $(cmd) to prevent word splitting
# SC2034: unused variable
# SC2155: declare and assign separately (masks return code)
# SC2004: $/${} unnecessary in arithmetic
# in script: disable inline
# shellcheck disable=SC2086
echo $unquoted_var
# disable for next line
# shellcheck disable=SC2086
echo $argslogging e diagnósticos
Um logger com níveis (DEBUG/INFO/WARN/ERROR) com timestamps e cores torna scripts muito mais fáceis de debugar. Sempre logue para stderr para que stdout permaneça limpo para dados. Suporte uma flag --verbose (ativa set -x) e --dry-run (imprime comandos sem executar). Códigos de cor ANSI: 31=red, 33=yellow, 32=green, 90=gray.
# logging function with levels
log() {
local level=$1; shift
local msg="$*"
local ts=$(date '+%Y-%m-%d %H:%M:%S')
local color
case $level in
DEBUG) color='\033[90m' ;;
INFO) color='\033[32m' ;;
WARN) color='\033[33m' ;;
ERROR) color='\033[31m' ;;
esac
printf "%b[%s] [%s] %s\033[0m\n" "$color" "$ts" "$level" "$msg" >&2
}
log INFO "Starting process"
log WARN "Disk space low"
log ERROR "Connection failed"
# verbose flag
[[ $VERBOSE ]] && set -x
# dry-run mode
if [[ $DRY_RUN ]]; then
echo "Would: $cmd"
else
eval "$cmd"
fiSegurança
quoting e injection
A regra #1 de segurança do bash: coloque toda expansão de variável entre aspas. $var sem aspas sofre word-splitting e globbing, levando a bugs e injection. Para comandos com args dinâmicos, use um array (cmd=(ls "$dir"); "${cmd[@]}") em vez de uma string. Nunca faça eval ou $() de entrada do usuário em uma string shell.
# ALWAYS quote variables
file="my file.txt"
rm $file # BAD: rm my file.txt (3 args!)
rm "$file" # GOOD: rm "my file.txt"
# use arrays for command + args
files=("a.txt" "b file.txt" "c.txt")
rm "${files[@]}" # each file as separate arg, safely
# avoid eval
eval "echo $user_input" # DANGEROUS
echo "$user_input" # safe
# avoid command substitution in unsafe places
# BAD: filename from user, executed
cmd="ls $userdir"
$cmd # word-splitting + globbing
# GOOD: use array
cmd=(ls "$userdir")
"${cmd[@]}"arquivos temporários seguros e segredos
mktemp cria arquivos com nomes imprevisíveis e perms 600 — seguros de symlink races. Sempre limpe com um trap. Leia segredos de variáveis de ambiente, nunca hardcode. Evite passar segredos como args de linha de comando (visíveis via ps) — use stdin, env vars, ou um arquivo de config com perms 600.
# safe temp files
tmp=$(mktemp) # atomic, unpredictable name
trap 'rm -f "$tmp"' EXIT
# safe temp dir
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
# secrets: read from env, never hardcode
api_key="${API_KEY:?Set API_KEY env var}"
# don't leak secrets to process list (ps)
# BAD: curl -H "Authorization: Bearer $TOKEN" ...
# GOOD: pass via stdin or config file
curl -H @- <<EOF https://api.example.com
Authorization: Bearer $TOKEN
EOF
# restrict file perms for secrets
umask 077
echo "$api_key" > ~/.secrets/api
chmod 600 ~/.secrets/apivalidação de entrada
Valide toda entrada do usuário antes de usar. Regex verifica formato; aritmética verifica ranges. Para paths de arquivo, rejeite .. (traversal) e paths absolutos a menos que explicitamente permitido. tr -cd remove caracteres não permitidos no set — útil para sanitizar identificadores. Whitelist (case) é mais segura que blacklist para ações permitidas.
# validate numeric input
read -p "Enter a number: " n
if ! [[ "$n" =~ ^[0-9]+$ ]]; then
echo "Not a number" >&2; exit 1
fi
# validate range
if (( n < 1 || n > 100 )); then
echo "Out of range" >&2; exit 1
fi
# validate file path (no traversal)
path="$1"
case "$path" in
*..*|/*) echo "Invalid path" >&2; exit 1 ;;
esac
# sanitize filename
safe=$(echo "$name" | tr -cd 'A-Za-z0-9._-')
# whitelist allowed values
case "$action" in
start|stop|restart) ;;
*) echo "Invalid action" >&2; exit 1 ;;
esacdrop de privilégio e setuid
Scripts shell setuid são inseguros — a maioria dos sistemas ignora o bit setuid em scripts. Use sudo com uma entrada sudoers restrita (comando específico, sem senha) em vez disso. Drope root o mais cedo possível via exec sudo -u. Para capabilities (ex.: bindar porta 80), use setcap em vez de rodar como root. chmod 700 mantém scripts privados ao owner.
# drop privileges in a script running as root
if [[ $EUID -eq 0 ]]; then
exec sudo -u appuser "$0" "$@"
fi
# check for root
if [[ $EUID -ne 0 ]]; then
echo "Requires root" >&2; exit 1
fi
# setuid scripts are DANGEROUS — avoid them
# instead, use sudoers entries
# /etc/sudoers.d/myapp:
# appuser ALL=(root) NOPASSWD: /usr/local/bin/myapp.sh
# restrict script permissions
chmod 700 script.sh # only owner can run
chown appuser:appuser script.sh
# use capsh instead of full root for fine-grained caps
# setcap 'cap_net_bind_service=+ep' /usr/bin/myappassinaturas e integridade
Sempre verifique checksums e assinaturas de scripts baixados, especialmente installers rodados como root. sha256sum -c compara contra um hash conhecido-bom. Assinaturas GPG provam tanto integridade quanto autenticidade (a identidade do signatário). Fazer pipe de curl para sha256sum permite inspecionar o hash antes de executar — mas o padrão mais seguro é baixar-depois-verificar-depois-executar.
# verify checksum
sha256sum -c file.sha256
# file.sha256 contains: <hash> <filename>
# generate checksums
sha256sum release.tar.gz > release.tar.gz.sha256
sha256sum *.iso > isos.sha256
# GPG sign and verify
gpg --sign file.txt # creates file.txt.gpg
gpg --verify file.txt.gpg
gpg --detach-sig --armor file.tar.gz
gpg --verify file.tar.gz.asc file.tar.gz
# download + verify in one go
curl -fsSL https://example.com/install.sh | sha256sum
curl -fsSL https://example.com/install.sh -o install.sh
sha256sum -c <(echo "expected_hash install.sh")Networking
curl Avançado
curl transfere dados via URLs. -X define método, -d envia dados, -H define headers. -O salva com nome de arquivo remoto. -I busca apenas headers. -L segue redirecionamentos. -u user:pass para auth. -v para verbose.
# POST with data
curl -X POST -d "name=Alice" https://api.example.com
# JSON POST
curl -H "Content-Type: application/json" \
-d '{"name":"Alice"}' https://api.example.com
# Download with progress
curl -O https://example.com/file.zip
# Headers only
curl -I https://example.comwget
wget baixa arquivos não-interativamente. -m espelha sites. -c resume downloads interrompidos. -r recursa, -l limita profundidade. -b roda em background. Melhor que curl para downloads recursivos.
# Download file
wget https://example.com/file.zip
# Mirror website
wget -m https://example.com
# Resume download
wget -c https://example.com/largefile.zip
# Recursive download
wget -r -l 2 https://example.com
# Background
wget -b https://example.com/file.zipssh & scp
ssh conecta a máquinas remotas. -i especifica um arquivo de chave. -L cria port forwarding local. scp copia arquivos sobre SSH. -r recursa diretórios. Use ssh-copy-id para instalar chaves. Configure ~/.ssh/config para aliases.
# SSH connect
ssh user@host
# SSH with key
ssh -i ~/.ssh/id_rsa user@host
# Port forwarding
ssh -L 8080:localhost:80 user@host
# Copy file
scp file.txt user@host:/path/
# Copy directory
scp -r dir/ user@host:/path/netstat & ss
netstat e ss mostram conexões de rede. -t TCP, -u UDP, -l listening, -n numérico, -p processo. ss é mais rápido e mais detalhado que netstat. Use para encontrar qual processo usa uma porta ou diagnosticar problemas de conexão.
# List listening ports
netstat -tlnp
ss -tlnp
# Show all connections
netstat -an
ss -tunap
# Show routing table
netstat -r
# Show interface stats
netstat -iping & traceroute
ping testa reachability e latência (-c limita a contagem). traceroute mostra o path até um host. dig consulta registros DNS (A, MX, NS, TXT). +short dá saída concisa. Use para diagnósticos de rede e troubleshooting de DNS.
# Check connectivity
ping -c 4 google.com
# Trace route
traceroute google.com
# DNS lookup
dig example.com
dig +short example.com
# Get DNS records
dig MX example.com
dig NS example.comShell Scripting Aprofundado
Funções
Funções agrupam comandos. local cria variáveis com escopo de função. $1, $2 são argumentos. return define exit status (0-255). Capture saída com $(). Funções devem ser definidas antes do uso. Use source para carregar de arquivos.
greet() {
local name="$1"
echo "Hello, $name"
return 0
}
greet "Alice"
# Return value via $?
result=$(greet "Bob")Condicionais
[ ] é o comando test. -f arquivo, -d diretório, -z string vazia, -n não-vazia. = para strings, -eq/-ne/-gt/-lt para números. Sempre coloque variáveis entre aspas para lidar com espaços. [[ ]] é bash aprimorado com regex.
if [ -f "/path/file" ]; then
echo "File exists"
elif [ -d "/path" ]; then
echo "Directory exists"
else
echo "Not found"
fi
# String comparison
if [ "$str" = "hello" ]; then ... fi
# Numeric
if [ "$num" -gt 10 ]; then ... fiLoops
for itera sobre listas. for estilo C usa (( )). while lê até a condição falhar. while read processa arquivos linha por linha com segurança. Sempre coloque variáveis entre aspas. Use find para iteração recursiva de arquivos.
# For loop
for i in 1 2 3; do
echo $i
done
# C-style
for ((i=0; i<5; i++)); do echo $i; done
# While loop
while read line; do
echo "$line"
done < file.txt
# Iterate files
for f in *.txt; do process "$f"; doneArrays
Arrays usam parênteses. ${arr[@]} expande todos os elementos. ${#arr[@]} é o comprimento. Sempre coloque "${arr[@]}" entre aspas para lidar com espaços. declare -A cria arrays associativos (bash 4+). Use read -a para dividir em array.
# Declare array
fruits=("apple" "banana" "cherry")
# Access
echo ${fruits[0]} # apple
echo ${fruits[@]} # all
echo ${#fruits[@]} # length
# Iterate
for fruit in "${fruits[@]}"; do
echo "$fruit"
done
# Associative array
declare -A config
config[key]="value"Tratamento de Erros
set -e sai em erro, -u em vars indefinidas, pipefail captura falhas de pipe. trap ERR trata erros. trap EXIT roda limpeza. command -v verifica se um comando existe. Sempre comece scripts com set -euo pipefail.
set -e # Exit on error
set -u # Error on undefined variable
set -o pipefail # Pipeline fails if any command fails
set -euo pipefail # All three (recommended)
trap 'echo "Error on line $LINENO"' ERR
trap 'cleanup' EXIT
# Check command result
if ! command -v docker &> /dev/null; then
echo "docker not installed"
fiOperações de Arquivo Aprofundado
find Avançado
find busca arquivos por critérios. -type f/d para arquivos/dirs. -size +1M maior que 1MB. -mtime -7 modificado dentro de 7 dias. -exec roda um comando em cada correspondência. {} é o nome do arquivo; \; encerra o comando.
# By name
find . -name "*.txt"
# By type and size
find . -type f -size +1M
# By modification time
find . -mtime -7 # Modified in last 7 days
# Execute command
find . -name "*.log" -exec gzip {} \;
# Delete
find . -name "*.tmp" -deletetar & Compressão
tar combina arquivos; -z comprime com gzip. -c create, -x extract, -t list, -v verbose, -f filename. Para .tar.bz2 use -j. zip/unzip lidam com formato ZIP. Use -C para extrair para um diretório específico.
# Create tar.gz
tar -czvf archive.tar.gz dir/
# Extract
tar -xzvf archive.tar.gz
# List contents
tar -tzvf archive.tar.gz
# zip/unzip
zip -r archive.zip dir/
unzip archive.ziprsync
rsync sincroniza arquivos eficientemente, transferindo apenas diferenças. -a archive mode (preserva atributos), -v verbose, -z compress. --delete remove arquivos não na fonte. A barra final em src/ importa: com ela, copia conteúdos; sem ela, copia o diretório.
# Sync local
rsync -av src/ dest/
# Sync to remote
rsync -avz src/ user@host:/path/
# Delete files not in source
rsync -av --delete src/ dest/
# Dry run
rsync -av --dry-run src/ dest/
# Exclude
rsync -av --exclude='*.log' src/ dest/Permissões de arquivo
Permissões: r=4, w=2, x=1. 755 = rwxr-xr-x (owner full, outros read/execute). 644 = rw-r--r-- (arquivos). u/g/o/a = user/group/other/all. chmod -R recursa. chown muda ownership.
# Symbolic
chmod u+x file # User execute
chmod g-w file # Group write off
chmod a=r file # All read only
# Numeric
chmod 755 file # rwxr-xr-x
chmod 644 file # rw-r--r--
# Change owner
chown user:group file
# Recursive
chmod -R 755 dir/ln & symlinks
Hard links apontam para o mesmo inode (mesmo sistema de arquivos apenas). Symbolic links (-s) apontam para um path. Hard links sobrevivem à deleção do original. Symlinks quebram se o alvo se move. Use readlink para resolver symlinks.
# Hard link
ln file.txt hardlink.txt
# Symbolic link
ln -s /path/to/target symlink
# Find broken symlinks
find . -type l ! -exec test -e {} \; -print
# Copy symlink target
cp -L symlink dest/Pegadinhas Comuns
Variáveis Sem Aspas
Variáveis sem aspas dividem em espaços e glob. $(ls) quebra com nomes de arquivo contendo espaços. Use globs (*.txt) ou find -print0 com read -d. Sempre coloque variáveis entre aspas: "$var". Use IFS= read -r para leitura segura de linhas.
# BUG: breaks with spaces
for f in $(ls); do echo "$f"; done
# GOOD: handle spaces
for f in *.txt; do echo "$f"; done
# Or with find
find . -name "*.txt" -print0 | while IFS= read -r -d '' f; do
echo "$f"
donecp vs mv
cp copia, mv move. Ambos podem sobrescrever sem aviso. Use -i para pedir antes de sobrescrever. Use -n para nunca sobrescrever. mv é atômico no mesmo sistema de arquivos, útil para locks e updates atômicos.
# cp: copy (original remains)
cp file.txt backup.txt
# mv: move/rename (original removed)
mv file.txt newname.txt
# Common mistake: overwriting
cp important.txt /tmp/ # If /tmp/important.txt exists, overwritten!
# Use -i for safety
cp -i file.txt /tmp/Perigos do rm
rm -rf é perigoso, especialmente com variáveis. Variáveis vazias causam rm -rf /. Sempre coloque variáveis entre aspas e verifique. Considere trash-cli para deleção recuperável. Nunca rode rm -rf / ou com sudo descuidadamente.
# DANGEROUS: recursive force
rm -rf / # Never do this!
rm -rf $VAR/ # If $VAR is empty, becomes rm -rf /
# Safer: check first
if [ -n "$VAR" ]; then
rm -rf "$VAR/"
fi
# Use trash-cli instead
trash-put file.txtShebang
O shebang (#!) especifica o interpretador. #!/usr/bin/env bash é mais portável que #!/bin/bash. Use /bin/sh para máxima portabilidade (evita bashisms). Sempre torne scripts executáveis com chmod +x.
#!/bin/bash # bash script
#!/usr/bin/env bash # Portable bash
#!/usr/bin/env python3 # Python
#!/bin/sh # POSIX sh (more portable)
# Make executable
chmod +x script.sh
./script.shExit Status
$? contém o exit status do último comando. 0 = sucesso, non-zero = falha. Verifique imediatamente antes que outro comando o sobrescreva. Melhor usar if command diretamente. Sempre saia com códigos de status significativos em scripts.
# Check last command
if [ $? -eq 0 ]; then
echo "Success"
fi
# Better: direct check
if command; then
echo "Success"
else
echo "Failed"
fi
# Exit with status
exit 0 # Success
exit 1 # FailureSnippets de Bash relacionados
Copy-paste ready code for common tasks.
Variáveis e Arrays em Bash
Atribuir variáveis, usar substituição de comandos e trabalhar com arrays em Bash.
Operações de Arquivo
Gestão de arquivos e diretórios.
Processamento de Texto
Processamento de texto com grep, sed, awk.
Loops
Loops for e while.
Condicionais
Instruções condicionais if e case.
Funções
Definição de função e parâmetros.
Arrays
Operações de array Bash.
Operações de String
Processamento de string Bash.
Git Básico
Operações básicas do Git.
Branches do Git
Gestão e operações de branch.
Merge do Git
Merge e resolução de conflitos.
Revert do Git
Desfazer commits e reverter.
Tags do Git
Gestão de tags de versão.
Git Stash
Stash de mudanças do diretório de trabalho.
Git Cherry-pick
Mesclar commits seletivamente.
Git Bisect
Busca binária para localizar commits com problemas.
Was this helpful?