CLI AI

sed -i não funciona no Mac? Diferenças BSD e GNU

2026-05-15

Se apareceu sed: 1: "s/foo/bar/g": invalid command code, nada está quebrado. O macOS traz utilitários BSD e seu script foi escrito para GNU. Mesmos nomes, flags diferentes. Estas são as seis que realmente mordem.

1. No macOS o sed -i exige um argumento

clai
$ clai substitua foo por bar direto no config.txt, funcionando no mac e no linux→ sed -i.bak 's/foo/bar/g' config.txt   # portável: gera config.txt.bak

O GNU trata o sufixo de backup como opcional; o BSD exige. sed -i '' 's/…' funciona no macOS e falha no Linux. sed -i.bak funciona nos dois — você só fica com um backup para apagar.

2. Regex estendida: use -E, nunca -r

clai
$ clai troque qualquer sequência de dígitos por N usando regex estendida→ echo "foo123" | sed -E 's/[0-9]+/N/'fooN

-r é exclusivo do GNU. -E funciona nos dois — o GNU aceita há anos. Faça do -E o seu padrão e uma classe inteira de bugs desaparece.

3. date: -d no Linux, -r no macOS

clai
$ clai converta o timestamp unix 1735689600 para uma data legível em UTC→ date -u -d @1735689600 '+%Y-%m-%d %H:%M:%S UTC'    # GNU2025-01-01 00:00:00 UTC→ date -u -r 1735689600 '+%Y-%m-%d %H:%M:%S UTC'     # BSD / macOS

Aqui não existe forma portável. Ramifique pelo uname ou instale o GNU coreutils e chame gdate.

4. stat: -c no Linux, -f no macOS

clai
$ clai imprima o tamanho e o nome deste arquivo→ stat -c '%s %n' t.txt      # GNU4 t.txt→ stat -f '%z %N' t.txt      # BSD / macOS

Muda a flag e os especificadores de formato. Se o script faz algo não trivial com stat, use find -printf no Linux ou wc -c quando precisar de portabilidade.

5. grep -P não existe no macOS

clai
$ clai extraia só os dígitos desta string, de forma portável→ echo "abc123" | grep -Eo '[0-9]+'123

grep -P (regex Perl) é uma opção de compilação do GNU e o macOS não a inclui. -E cobre a maioria dos padrões reais; se você precisa mesmo de lookahead, precisa de perl ou ripgrep, não de grep.

6. xargs -r é só do GNU — e você precisa dele

clai
$ clai ache .log com mais de 30 dias e apague, sem rodar rm com lista vazia→ find . -name '*.log' -mtime +30 -print0 | xargs -0 -r rm

Sem -r, o xargs do GNU executa o comando uma vez sem argumento nenhum quando a entrada é vazia — é assim que xargs rm num diretório vazio vira erro e xargs docker rm vira surpresa. O xargs do BSD já não roda com entrada vazia, então lá o -r é desnecessário e em versões antigas nem é reconhecido.

Pegadinhas

  • brew install coreutils instala gêmeos com prefixo g — gsed, gdate, gstat. Colocar gnubin no PATH faz o Mac se comportar como Linux: conserta o seu laptop e esconde o bug do resto do time.
  • Não conte com readlink -f. Em macOS antigos ele não existe. Use realpath onde houver, ou greadlink -f.
  • base64 -d versus -D. O GNU decodifica com -d; o macOS historicamente quer -D.

Perguntas relacionadas

O que significa «invalid command code» de verdade? O sed do BSD engoliu a palavra seguinte como sufixo de backup e leu o resto da sua expressão como um comando que não conhece. É vítima do parsing de argumentos, não problema de regex.

macOS é BSD ou Linux? Nenhum dos dois. O userland descende do FreeBSD, por isso as ferramentas compartilham nome com as do Linux mas não as flags.

Como escrevo um script que roda nos dois? Fique em flags POSIX, prefira -E, escreva sempre sed -i.bak e teste nos dois antes de chegar na CI.

Veja também

O CliAI detecta o sistema e o shell em que você está e gera as flags que aquela máquina realmente aceita. Instale em uma linha.