githubEditar

Bash Pitfalls

Fonte: BashPitfalls - Greg's Wiki (mywiki.wooledge.org) Última Atualização: Agosto 2025 Autor: Greg Wooledge (GreyCat)

Introdução

Este é um guia abrangente dos erros mais comuns cometidos por programadores Bash. Cada exemplo mostrado aqui contém falhas de alguma forma. O objetivo é ajudar desenvolvedores a evitar essas armadilhas e escrever scripts Bash mais seguros e confiáveis.


Detalhamento dos Pitfalls

1. for f in $(ls *.mp3) {#1-for-f-in-ls-mp3}

❌ Problema:

for f in $(ls *.mp3); do # Errado!
    some command $f      # Errado!
done

🔍 Por que está errado:

  1. Word Splitting: Nomes de arquivos com espaços são divididos

  2. Globbing: Caracteres como * são expandidos

  3. Parsing: Não há como distinguir onde um nome termina e outro começa

  4. ls behavior: O comando ls pode alterar nomes de arquivos

  5. Newlines: Arquivos podem conter quebras de linha

  6. Leading dashes: Arquivos começando com - causam problemas

✅ Solução correta:

2. cp $file $target {#2-cp-file-target}

❌ Problema:

🔍 Por que está errado:

  • Word Splitting: Variáveis sem aspas sofrem divisão de palavras

  • Pathname expansion: Caracteres como *, ?, [ são expandidos

  • Leading dashes: Arquivos começando com - são interpretados como opções

✅ Solução correta:

3. Filenames with leading dashes {#3-filenames-with-leading-dashes}

❌ Problema:

✅ Soluções:

4. [ $foo = "bar" ] {#4-foo-bar}

❌ Problema:

🔍 Problemas:

  • Se $foo está vazio: [ = "bar" ] → erro "unary operator expected"

  • Se $foo tem espaços: [ hello world = "bar" ] → muitos argumentos

✅ Soluções:

5. cd $(dirname "$f") {#5-cd-dirname-f}

❌ Problema:

✅ Solução:

🔍 Explicação das aspas aninhadas:

  • Aspas dentro de $() são um par separado

  • Aspas fora de $() são outro par separado

  • O parser trata como níveis de aninhamento diferentes

6. [ "$foo" = bar && "$bar" = foo ] {#6-foo-bar-bar-foo}

❌ Problema:

✅ Soluções:

❌ Evite (obsoleto):

7. [[ $foo > 7 ]] {#7-foo-7}

❌ Problema:

✅ Soluções:

⚠️ Cuidado com injeção de código: Se $foo vem de fonte externa não confiável, use apenas [ "$foo" -gt 7 ].

8. grep foo bar | while read -r; do ((count++)); done {#8-grep-while-read}

❌ Problema:

🔍 Por que: O while roda em uma subshell devido ao pipe, então mudanças em count não persistem.

✅ Soluções:

9. if [grep foo myfile] {#9-if-grep-foo-myfile}

❌ Problema:

🔍 Entendimento incorreto: Muitos iniciantes pensam que [ é parte da sintaxe do if, mas [ é um comando!

✅ Solução:

10. if [bar="$foo"]; then … {#10-if-bar-foo}

❌ Problema:

✅ Solução:

🔍 Explicação: [ é um comando que precisa de espaços entre seus argumentos, como qualquer comando.

11. if [ [ a = b ] && [ c = d ] ]; then … {#11-if-a-b-c-d}

❌ Problema:

✅ Soluções:

12. read $foo {#12-read-foo}

❌ Problema:

✅ Solução:

13. cat file | sed s/foo/bar/ > file {#13-cat-sed-same-file}

❌ Problema:

🔍 Resultado: O arquivo é truncado antes da leitura, resultando em perda de dados.

✅ Soluções:

14. echo $foo {#14-echo-foo}

❌ Problema:

✅ Soluções:

🔍 Demonstração:

15. $foo=bar {#15-foo-bar-assignment}

❌ Problema:

✅ Solução:

16. foo = bar {#16-foo-bar-spaces}

❌ Problema:

✅ Solução:

17. echo <<EOF {#17-echo-eof}

❌ Problema:

✅ Soluções:

18. su -c 'some command' {#18-su-c-some-command}

❌ Problema:

✅ Solução:

19. cd /foo; bar {#19-cd-foo-bar}

❌ Problema:

✅ Soluções:

20. [ bar == "$foo" ] {#20-bar-foo-double-equals}

❌ Problema:

✅ Soluções:

21. for i in {1..10}; do ./something &; done {#21-for-i-background}

❌ Problema:

✅ Soluções:

22. cmd1 && cmd2 || cmd3 {#22-cmd1-cmd2-cmd3}

❌ Problema:

🔍 Demonstração:

✅ Solução:

23. echo "Hello World!" {#23-echo-hello-world}

❌ Problema (apenas shells interativos):

✅ Soluções:

24. for arg in $* {#24-for-arg-in-star}

❌ Problema:

✅ Soluções:

25. function foo() {#25-function-foo}

❌ Problema:

✅ Soluções:

26. echo "~" {#26-echo-tilde}

❌ Problema:

✅ Soluções:

27. local var=$(cmd) {#27-local-var-cmd}

❌ Problemas:

✅ Solução:

28. export foo=~/bar {#28-export-foo-tilde}

❌ Problema:

✅ Soluções:

29. sed 's/$foo/good bye/' {#29-sed-variable-substitution}

❌ Problema:

✅ Solução:

30. tr [A-Z] [a-z] {#30-tr-a-z}

❌ Problemas:

✅ Soluções:

31. ps ax | grep gedit {#31-ps-grep}

❌ Problema:

✅ Soluções:

32. printf "$foo" {#32-printf-foo}

❌ Problema:

✅ Solução:

33. for i in {1..$n} {#33-for-i-brace-expansion}

❌ Problema:

✅ Solução:

34. if [[ $foo = $bar ]] (depending on intent) {#34-pattern-matching}

❌ Problema:

✅ Soluções:

35. if [[ $foo =~ 'some RE' ]] {#35-regex-quotes}

❌ Problema:

✅ Soluções:

36. [ -n $foo ] or [ -z $foo ] {#36-test-n-z}

❌ Problema:

✅ Soluções:

❌ Problema:

✅ Solução:

38. ed file <<<"g/d{0,3}/s//e/g" fails {#38-ed-regex}

❌ Problema:

✅ Solução:

39. expr sub-string fails for "match" {#39-expr-match}

❌ Problema:

✅ Soluções:

40. On UTF-8 and Byte-Order Marks (BOM) {#40-utf8-bom}

⚠️ Problema: Arquivos com BOM podem quebrar scripts que esperam caracteres ASCII específicos no início.

✅ Dica:

  • Scripts Unix/UTF-8 normalmente NÃO usam BOM

  • Se encontrar BOM, trate como arquivo estrangeiro (tipo Windows)

  • Remove BOM se necessário antes de processar

41. content=$(<file) {#41-content-file}

⚠️ Cuidado:

✅ Para preservar exatamente:

42. for file in ./* ; do if [[ $file != . ]] {#42-glob-pattern}

❌ Problema:

✅ Soluções:

43. somecmd 2>&1 >>logfile {#43-redirection-order}

❌ Problema:

✅ Solução:

🔍 Explicação: Redirecionamentos são avaliados da esquerda para direita:

  • 2>&1 >>logfile: stderr → terminal, depois stdout → logfile

  • >>logfile 2>&1: stdout → logfile, depois stderr → onde stdout aponta (logfile)

44. cmd; (( ! $? )) || die {#44-exit-status}

❌ Desnecessário:

✅ Solução:

45. y=$(( array[$x] )) {#45-array-arithmetic}

❌ Problema:

✅ Soluções:

46. read num; echo $((num+1)) {#46-read-arithmetic}

❌ Problema:

✅ Solução:

47. IFS=, read -ra fields <<< "$csv_line" {#47-ifs-csv}

❌ Problema:

✅ Solução:

Atualizado