Você já se perguntou por que um comando funciona perfeitamente no Linux mas falha no macOS? Por exemplo, este comando deveria ter funcionado no Linux:
sed -i 's/debug=true/debug=false/' config.ini
Eu já tinha rodado esse comando várias vezes no Linux. Funciona todas as vezes.
No entanto, o mesmo comando falhou no macOS. Verifiquei a sintaxe, verifiquei o arquivo e rodei o comando de novo. Mas falhou.
Linux e macOS oferecem ambos um ambiente de linha de comando do tipo Unix. Essa semelhança torna fácil supor que comandos Linux vão se comportar da mesma forma no Terminal do macOS. Muitas vezes se comportam, mas nem sempre.
Este artigo explica por que comandos Linux às vezes se comportam de forma diferente no macOS. Você vai entender de onde vêm essas diferenças, por que elas existem e como reconhecê-las antes que afetem seus scripts shell ou seu fluxo de trabalho na linha de comando.
No começo, tudo parece familiar
O comando sed que falhou não foi uma exceção aleatória. A maioria dos comandos Linux funciona no macOS sem nenhuma mudança.
Essa consistência cria uma expectativa. Se um comando funciona, o próximo também deveria funcionar.
A expectativa é razoável. Linux e macOS oferecem ambos um ambiente de linha de comando do tipo Unix. Um sistema operacional do tipo Unix segue muitas das interfaces e princípios de projeto introduzidos pelo Unix. Como resultado, os dois sistemas expõem muitas das mesmas ferramentas de linha de comando e os mesmos layouts de diretório familiares.
Abra o Terminal do macOS e você vai reconhecer comandos como ls, cd, pwd, cp, mv e ssh. Você também vai encontrar diretórios familiares como /usr, /etc, /var e /tmp.
Para tarefas do dia a dia, a experiência costuma ser a mesma. Isso torna fácil supor que as ferramentas subjacentes também se comportem da mesma forma.
É nessa suposição que as diferenças começam.
O mesmo comando nem sempre significa o mesmo programa
Um nome de comando diz ao shell o que executar. Ele não diz como aquele comando foi implementado.
Linux e macOS compartilham muitos nomes de comandos porque ambos seguem o POSIX. O POSIX é um padrão que define interfaces de linha de comando e comportamentos de sistema comuns para sistemas operacionais do tipo Unix. Ele especifica o que muitos comandos devem fazer, mas não exige que toda implementação suporte as mesmas extensões ou opções de linha de comando.
No Linux, comandos como sed, grep, find e tar geralmente vêm dos utilitários GNU. Os utilitários GNU são uma coleção de ferramentas de linha de comando desenvolvidas pelo Projeto GNU.
No macOS, muitos desses mesmos comandos vêm dos utilitários BSD. Os utilitários BSD oferecem a mesma funcionalidade central, mas foram desenvolvidos de forma independente. Como resultado, algumas opções, padrões e recursos diferem.
Considere o comando sed da introdução.
No Linux:
sed -i 's/debug=true/debug=false/' config.ini
Rodar o comando acima no macOS gera um erro como:
sed: 1: "config.ini": extra characters at the end of s command
Isso acontece porque o sed do BSD interpreta config.ini como o argumento de sufixo de backup e então não sobra nada para tratar como o arquivo real, então ele tenta ler s/debug=true/debug=false/ como um arquivo/script e trava.
Para fazer funcionar no macOS, você precisa de uma string vazia como argumento explícito de sufixo de backup:
sed -i '' 's/debug=true/debug=false/' config.ini
Os dois comandos editam o arquivo no lugar. A diferença é a opção -i. A opção -i não é definida pelo POSIX. O sed do GNU e o sed do BSD a adicionaram de forma independente, e cada um escolheu uma sintaxe diferente. O sed do GNU permite -i sem extensão de backup. O sed do BSD exige uma extensão de backup. Uma string vazia ('') diz ao sed do BSD para não criar backup.
sed é apenas um exemplo. Comandos como find, date, stat, xargs e tar também incluem extensões específicas de GNU e de BSD. A maioria das diferenças é pequena. Elas se tornam importantes quando você copia comandos entre Linux e macOS ou escreve scripts shell que precisam rodar nos dois sistemas.
Ponto-chave:
O nome do comando muitas vezes é o mesmo. A implementação por trás dele pode não ser. Essa distinção explica muitas das diferenças de comandos entre Linux e macOS.
Quando scripts shell deixam de ser portáveis
Um comando que funciona no terminal nem sempre é portável em um script shell.
Um script shell é um arquivo de texto que contém comandos para o shell executar. Scripts combinam vários comandos, então têm mais chance de depender de comportamentos que diferem entre Linux e macOS.
Algumas diferenças produzem um erro imediatamente. Essas costumam ser fáceis de corrigir.
Problemas mais difíceis aparecem quando um script roda com sucesso mas produz uma saída inesperada. O script parece correto, mas o resultado está errado.
Considere este comando:
find . -type f -printf '%f\n'
Em muitos sistemas Linux, ele imprime o nome de cada arquivo correspondente.
No macOS, ele falha porque a implementação BSD do find não suporta a opção -printf. -printf é uma extensão GNU, não faz parte do padrão POSIX.
Se você rodar o comando acima no macOS (BSD find), vai obter um erro como o abaixo:
find: -printf: unknown primary or operator
Esse é um problema de portabilidade comum. Um script pode funcionar perfeitamente em um sistema operacional porque depende de uma extensão que não existe em outro.
Se um script shell precisa rodar tanto no Linux quanto no macOS, escreva-o conforme o padrão POSIX sempre que for prático. O POSIX define um conjunto comum de interfaces para sistemas operacionais do tipo Unix. Recursos fora desse padrão, muitas vezes chamados de extensões, podem não existir ou se comportar de forma diferente em outra plataforma. A Apple recomenda evitar extensões específicas de plataforma ao escrever scripts shell multiplataforma.
Ponto-chave:
Um script shell portável depende de mais do que nomes de comandos. Ele também depende de usar recursos definidos pelo POSIX ou disponíveis em todo sistema operacional que você pretende suportar.
Por que sudo nem sempre resolve o problema
No Linux, um erro de permissão geralmente significa que o usuário atual não tem privilégios suficientes. Rodar o comando com sudo normalmente resolve o problema.
No macOS, essa abordagem nem sempre funciona.
Você ainda pode ver um erro como:
Operation not permitted
mesmo rodando o comando com sudo.
A razão é que o macOS adiciona camadas de segurança além das permissões de arquivo tradicionais do Unix.
Uma dessas camadas é o System Integrity Protection (SIP). O SIP protege partes críticas do sistema operacional contra modificação. Ele também restringe mudanças em arquivos e diretórios protegidos, mesmo para processos rodando com privilégios de root. A Apple projetou o SIP para reduzir o impacto de software malicioso e de mudanças acidentais no sistema.
Outra camada é o Transparency, Consent, and Control (TCC). O TCC é o framework de privacidade da Apple. Ele controla o acesso a dados sensíveis do usuário, como as pastas Desktop, Documents e Downloads. O TCC avalia o aplicativo que solicita acesso, não apenas a conta de usuário que roda o comando.
É por isso que sudo às vezes não é suficiente. Um comando no Terminal ainda pode falhar porque o próprio Terminal não recebeu permissão para acessar os dados protegidos. Em muitos casos, conceder Full Disk Access ao Terminal ou ao seu aplicativo de terminal resolve erros de acesso relacionados ao TCC.
Nem todo erro Operation not permitted é causado pelo SIP ou pelo TCC. Permissões de arquivo tradicionais, flags de arquivo ou outras políticas de segurança também podem produzir o mesmo erro. Para usuários Linux migrando para o macOS, porém, SIP e TCC estão entre os motivos mais comuns de o sudo não se comportar como esperado.
Ponto-chave:
No Linux, sudo muitas vezes é suficiente para resolver um problema de permissão. No macOS, privilégios elevados são apenas uma parte do modelo de segurança. SIP e TCC ainda podem impedir um comando de acessar ou modificar recursos protegidos.
Como desenvolvedores Linux se adaptam ao macOS
Desenvolvedores experientes raramente tentam fazer o macOS se comportar exatamente como o Linux. Eles constroem um ambiente de desenvolvimento previsível.
O primeiro passo é entender a variável de ambiente PATH. PATH é uma lista ordenada de diretórios que o shell procura para localizar um executável. Se vários executáveis têm o mesmo nome, o shell roda o primeiro que encontrar.
Muitos desenvolvedores usam o gerenciador de pacotes Homebrew para instalar ferramentas de linha de comando adicionais. Ele instala software sem substituir os utilitários que vêm com o sistema operacional.
Uma instalação comum é o GNU Coreutils. O GNU Coreutils é uma coleção de utilitários de linha de comando padrão, incluindo ls, cp, mv e sort. Para evitar conflito com os utilitários BSD que o macOS fornece, o Homebrew instala muitos comandos com um prefixo g, como gls e gcp. Se você prefere os nomes de comando padrão, pode configurar seu PATH para procurar o diretório gnubin do Homebrew antes dos diretórios do sistema.
A mesma abordagem se aplica a outros utilitários GNU, incluindo gnu-sed, grep e findutils. Instalá-los torna as implementações GNU disponíveis. Seu shell só as usa se você chamar o comando com prefixo, como gsed, ou configurar o PATH para dar prioridade à versão GNU.
Instalar utilitários GNU não muda como o macOS funciona. Isso muda qual implementação o seu shell executa.
Ponto-chave:
O Homebrew torna ferramentas GNU disponíveis, mas o PATH determina qual implementação o seu shell executa. Um ambiente de desenvolvimento previsível começa por saber a qual executável seus comandos resolvem.
Conclusão
Linux e macOS compartilham muitos nomes de comandos porque ambos oferecem um ambiente de linha de comando do tipo Unix. Essa semelhança torna fácil esperar o mesmo comportamento.
Um nome de comando diz ao shell o que executar. Ele não diz como aquele comando foi implementado nem de quais recursos específicos de plataforma ele depende. O POSIX define um conjunto comum de interfaces para sistemas operacionais do tipo Unix, mas também permite que implementações forneçam suas próprias extensões. Como resultado, dois comandos com o mesmo nome podem se comportar de forma diferente em sistemas operacionais diferentes.
Esse mesmo princípio explica o restante das diferenças que você viu. Algumas vêm dos utilitários GNU e BSD. Outras vêm de recursos de segurança do macOS, como o System Integrity Protection (SIP) e o Transparency, Consent, and Control (TCC). Scripts shell se tornam menos portáveis quando dependem de extensões que não estão disponíveis em todos os lugares.
Nenhuma dessas diferenças é aleatória. Cada uma segue uma decisão de projeto tomada pelo sistema operacional ou pelo utilitário que implementa o comando.
Depois de entender isso, depurar fica muito mais fácil. Em vez de perguntar "Por que este comando falhou?", pergunte "Que suposições este comando faz sobre a plataforma?".
Essa pergunta costuma levar à resposta muito mais rápido.
Ponto-chave:
Linux e macOS compartilham muitos nomes de comandos, mas nem sempre compartilham o mesmo comportamento. Entender a implementação, o padrão e o sistema operacional por trás de um comando é a forma mais confiável de explicar por que ele se comporta de forma diferente.
Perguntas frequentes (FAQ)
P: Por que comandos Linux nem sempre funcionam no macOS?
R: Não. O macOS não é baseado no Linux. O macOS é construído sobre o Darwin, o sistema operacional de código aberto da Apple. O Darwin combina o kernel XNU com componentes derivados do BSD e de outros projetos de código aberto. Linux e macOS são ambos sistemas operacionais do tipo Unix, mas usam kernels diferentes e utilitários de linha de comando diferentes.
P: Por que sed -i funciona no Linux mas não no macOS?
R: A maioria das distribuições Linux inclui o GNU sed, enquanto o macOS inclui o BSD sed.
Os dois suportam edição no lugar, mas usam sintaxes diferentes para a opção -i. O sed do GNU permite -i sem extensão de backup. O sed do BSD exige uma extensão de backup. Se você não quer um arquivo de backup, passe uma string vazia:
sed -i '' 's/foo/bar/' file.txt
P: Por que find -printf funciona no Linux mas não no macOS?
R: -printf é uma extensão GNU.
A implementação BSD do find incluída no macOS não a suporta porque ela não faz parte da especificação POSIX.
P: Por que alguns comandos Linux se comportam de forma diferente no macOS?
R: Linux e macOS oferecem ambos um ambiente de linha de comando do tipo Unix, então muitos nomes de comandos são iguais. No entanto, as implementações subjacentes muitas vezes diferem. A maioria das distribuições Linux traz utilitários GNU, enquanto o macOS traz utilitários BSD. Embora ambos sigam o POSIX para o comportamento central, eles suportam extensões, opções e padrões diferentes.
P: Por que sudo não corrige todo erro de permissão no macOS?
R: sudo concede privilégios elevados, mas o macOS também aplica mecanismos de segurança adicionais. Por exemplo, o System Integrity Protection (SIP) protege partes críticas do sistema operacional, e o Transparency, Consent, and Control (TCC) protege o acesso a dados sensíveis do usuário. Esses mecanismos podem negar acesso mesmo quando um comando roda com privilégios de root.
Nem todo erro Operation not permitted é causado pelo SIP ou pelo TCC, mas eles são motivos comuns de usuários Linux encontrarem problemas de permissão no macOS.
P: Posso fazer o macOS se comportar como o Linux?
R: Você pode tornar o ambiente de linha de comando mais familiar, mas não pode fazer o macOS se comportar exatamente como o Linux.
Muitos desenvolvedores instalam utilitários GNU com o Homebrew e configuram seu shell para usar essas ferramentas. Isso reduz problemas de compatibilidade, mas o macOS ainda usa seu próprio kernel, modelo de segurança e utilitários de sistema padrão.
P: Devo substituir os utilitários BSD pelos utilitários GNU?
R: Normalmente, não. O Homebrew instala utilitários GNU ao lado dos utilitários BSD que vêm com o macOS. Para muitas ferramentas, a versão GNU usa um prefixo g, como gsed, então não conflita com a versão do sistema. Você pode configurar seu PATH se quiser que seu shell prefira a implementação GNU.
P: Como posso verificar qual comando meu shell está rodando?
R: Use:
command -v sed
command -v é definido pelo POSIX e mostra qual executável seu shell vai rodar. Você também pode usar:
which sed
Muitos sistemas fornecem o which, mas command -v é a escolha mais portável para scripts shell.
P: Como posso escrever scripts shell que funcionem tanto no Linux quanto no macOS?
R: Use recursos POSIX sempre que for prático. Evite extensões específicas de GNU ou de BSD a menos que seu script tenha como alvo um sistema operacional específico. Se seu script precisa suportar tanto Linux quanto macOS, teste-o nas duas plataformas em vez de assumir comportamento idêntico.
P: O que é POSIX?
R: POSIX (Portable Operating System Interface) é um padrão que define interfaces e comportamentos comuns para sistemas operacionais do tipo Unix. Ele especifica como comandos e chamadas de sistema devem se comportar, mas permite que implementações adicionem suas próprias extensões.
P: Se Linux e macOS seguem o POSIX, por que ainda existem diferenças?
R: O POSIX define uma base comum, não todos os recursos. Muitas opções usadas com frequência são extensões GNU ou BSD e não fazem parte do padrão. Implementações diferentes podem, portanto, oferecer funcionalidades adicionais permanecendo compatíveis com o POSIX.
P: Qual é a diferença entre utilitários GNU e utilitários BSD?
R: Utilitários GNU são ferramentas de linha de comando desenvolvidas pelo Projeto GNU e comumente usadas no Linux.
Utilitários BSD têm origem no projeto Berkeley Software Distribution (BSD) e são incluídos com o macOS.
Ambos fornecem comandos semelhantes, mas algumas opções, padrões e comportamentos diferem.
P: O que torna um script shell portável?
R: Um script shell portável evita extensões específicas de GNU ou de BSD sempre que possível. Ele depende de recursos definidos pelo POSIX e é testado em todo sistema operacional que deve suportar.
P: O que é o System Integrity Protection (SIP)?
R: O System Integrity Protection (SIP) é um recurso de segurança do macOS que protege arquivos e diretórios críticos do sistema contra modificação, mesmo por processos rodando com privilégios de root.
P: O que é o Transparency, Consent, and Control (TCC)?
R: O Transparency, Consent, and Control (TCC) é o framework de privacidade da Apple. Ele controla o acesso de aplicativos a dados sensíveis do usuário, como arquivos nas pastas Desktop, Documents e Downloads, e exige consentimento do usuário antes que o acesso seja concedido.
P: Qual é a diferença entre permissões Unix e TCC?
R: Permissões Unix determinam se um usuário tem permissão para acessar um arquivo.
O TCC determina se um aplicativo tem permissão para acessar dados protegidos do usuário.
As duas verificações podem precisar ser bem-sucedidas no macOS.
P: O que é o Homebrew?
R: O Homebrew é um gerenciador de pacotes para macOS. Ele instala e gerencia software sem substituir os utilitários fornecidos pelo sistema operacional.
P: Por que comandos GNU instalados pelo Homebrew muitas vezes começam com g?
R: O Homebrew instala muitos utilitários GNU com um prefixo g, como gsed e gcp, para evitar conflito com as versões BSD já incluídas no macOS.
P: O que é a variável de ambiente PATH?
R: PATH é uma lista ordenada de diretórios que o shell procura para localizar programas executáveis. Quando vários executáveis têm o mesmo nome, o shell roda o primeiro que encontrar no PATH.
P: Por que instalar utilitários GNU não muda automaticamente o comportamento dos comandos?
R: Instalar utilitários GNU apenas os torna disponíveis. O shell continua usando o executável que aparecer primeiro no PATH. A menos que o PATH seja atualizado ou o comando GNU seja invocado explicitamente (por exemplo, gsed), a versão BSD ainda será executada.



