23 pontos por GN⁺ 2025-12-31 | 1 comentários | Compartilhar no WhatsApp
  • Apresenta um truque para executar arquivos Go diretamente como se fossem executáveis
  • Colocando //usr/local/go/bin/go run "$0" "$@"; exit na primeira linha e dando permissão de execução, é possível rodar com ./script.go
  • Esse método não é um shebang, e sim um uso do comportamento do POSIX de fazer fallback para o shell /bin/sh quando ocorre ENOEXEC
  • O shell executa a primeira linha como comando, enquanto o compilador Go a reconhece como comentário com // e a ignora
  • Com "$0", passa-se o caminho do próprio arquivo, então o go run compila e executa o script, e "$@" repassa os argumentos
  • A poderosa biblioteca padrão e a garantia de compatibilidade retroativa do Go o tornam adequado para scripting, e usando versões Go 1.x o script pode continuar funcionando por décadas
  • É possível evitar a complexidade da gestão de dependências do Python, como ambientes virtuais e pip/poetry/uv

Como funciona o shebang falso

  • O shebang (#!) especifica o interpretador via a syscall execve, mas a técnica apresentada neste texto não é um shebang
  • O formato é colocar //usr/local/go/bin/go run "$0" "$@"; exit na primeira linha do arquivo-fonte Go e, abaixo de package main, escrever código Go comum
    • Dando permissão de execução com chmod +x script.go, ele pode ser executado como ./script.go
  • Verificando com strace, quando o shell tenta executar ./script.go via execve, o kernel retorna ENOEXEC (Exec format error)
    • Ao receber ENOEXEC, o shell faz fallback para /bin/sh e interpreta o arquivo como script de shell
    • No shell, // não é comentário, e sim interpretado como caminho raiz (/), então //usr/local/go/bin/go é executado normalmente como caminho válido
  • Assim, a primeira linha //usr/local/go/bin/go run "$0" "$@"; exit é executada como comando pelo shell
    • "$0" passa o caminho do arquivo executado, então no comando de execução "$0" vira o caminho de script.go, e o go run localiza, compila e executa o próprio arquivo
    • "$@" é a expansão dos parâmetros posicionais a partir do primeiro argumento, permitindo chamadas como ./script.go -f flag0 here are some args
    • Sem ; exit, o sh continuaria interpretando o arquivo Go linha por linha e acabaria gerando erro em tokens como package

Por que Go é adequado para scripting

  • A garantia de compatibilidade retroativa do Go é o ponto central: usando Go 1.x, scripts escritos hoje continuam funcionando por muito tempo
  • A biblioteca padrão bem desenvolvida e as ferramentas embutidas (formatador, linter etc.) são fornecidas sem configuração extra, o que maximiza o compartilhamento e a portabilidade dos scripts
    • Diferentemente do Python, é possível executar o código sem precisar aprender sobre ambientes virtuais ou vários gerenciadores de pacotes (pip, poetry, uv)
    • Com as ferramentas embutidas do ecossistema Go e a integração com IDEs, dá para usar formatador e linter por padrão mesmo sem .pyproject ou package.json
  • Se o Go atual estiver instalado, ele pode ser executado em qualquer sistema operacional por décadas

Comparação com outras linguagens compiladas

  • Rust tem compilação lenta, biblioteca padrão mais fraca, depende mais de bibliotecas externas e sua busca por perfeição tende a reduzir a velocidade de desenvolvimento
  • Java e linguagens da JVM já contam com linguagens de script baseadas em bytecode da JVM, e scripting leve em Kotlin também pode ser uma alternativa
  • Entre as linguagens compiladas, o Go tem características especialmente adequadas para uso como linguagem de script

Problema de formatação do gopls e solução

  • O gopls exige espaço após comentários (//example// example), então a linha do shebang falso é quebrada
  • Se houver espaço, vira // usr/local/go/bin/go, e o shell deixa de reconhecer isso como caminho
  • Solução: usar a sugestão da thread no HN de comentário de bloco /**/ no lugar de //
    • Escrever no formato /*usr/local/go/bin/go run "$0" "$@"; exit; */
    • O ponto e vírgula (;) após exit é obrigatório

1 comentários

 
GN⁺ 2025-12-31
Comentários do Hacker News
  • A parte em que o autor diz “não quero me preocupar com pip vs poetry vs uv” na verdade é algo que o uv já atende diretamente para esse caso de uso
    Incluindo as dependências do PyPI, basta ter a versão do Python e o uv instalados
    Link para a documentação oficial do uv

    • Existe até uma forma melhor
      #!/usr/bin/env -S uv run --python 3.14 --script
      Assim, mesmo que o próprio Python não esteja instalado, o uv baixa a versão especificada e executa
    • Eu também pensava assim, mas para quem não usa Python isso ainda não é intuitivo
      Quando alguém começa com Clojure, normalmente recebe a orientação de usar Leiningen, mas ao pesquisar Python aparecem venv, poetry, hatch, uv e várias outras opções
      O uv está cada vez mais virando o padrão, mas ainda não é universal
      Já tive a experiência de instalar Go com apt e depois reinstalar porque a versão era antiga demais, mas isso foi resolvido muito mais rápido
      A questão dos ambientes virtuais no Python continua complexa
    • Eu resolvi esse problema em 2019 com o PyFlow
      Uma ferramenta OSS escrita em Rust que gerencia automaticamente a versão do Python e o venv
      Você configura apenas o pyproject.toml e executa pyflow main.py; ele instala e trava dependências como o Cargo, além de ajustar automaticamente a versão do Python para o projeto
      Na época, Poetry e Pipenv eram populares, mas ainda deixavam a desejar em venv e gerenciamento de versão
    • Eu também migrei quase tudo para uv
      Uso principalmente uv add e só recorro a uv pip quando preciso
      Mas o uv pip ainda herda as limitações do pip — a resolução de dependências muda conforme a ordem de instalação
      Instalar dep-a com uv pip install dep-a e depois dep-b, inverter a ordem, ou instalar tudo de uma vez gera resultados diferentes
      Isso é mais um problema do pip, mas a confusão no gerenciamento de pacotes Python continua
    • Na verdade, nem precisa especificar a versão do Python
      O uv baixa tudo automaticamente
  • O Go recusou explicitamente suporte a shebang
    Em vez disso, recomendam usar gorun
    Dá para executar com um truque POSIX como /// 2>/dev/null ; gorun "$0" "$@" ; exit $?
    Nim, Zig e D permitem algo parecido com a opção -run, e Swift, OCaml e Haskell conseguem executar o arquivo diretamente
    Link para a discussão relacionada

    • Para scripts pequenos, o interpretador yaegi pode ser melhor do que go run
      yaegi GitHub
  • O texto dizendo “não quero saber a diferença entre pip, poetry e uv, só quero rodar o código” no fim das contas é um problema de familiaridade técnica
    uv run e a PEP 723 já resolveram tudo

    • Sim, mas demorou demais para o uv run aparecer
      Uso Python há mais de 20 anos, mas sempre tive receio de qualquer codebase com pacotes externos ou venv
      Graças ao uv run, migrei todos os projetos da empresa, mas os pessoais já foram para Go
      No longo prazo, prefiro linguagens com tipagem estática
    • Em qualquer linguagem antiga, no fim você acaba aprendendo bibliotecas concorrentes
    • Isso é um problema de UX
      O usuário só quer que o programa funcione
      uv run e PEP 723 resolvem o problema, mas ainda existe barreira de entrada porque é preciso conhecer o uv
      Enquanto o uv não for a ferramenta padrão oficial, muita gente vai abandonar Python
  • Acho isso uma ideia realmente genial
    Mas scripting exige uma noção de ergonomia diferente da de software para distribuição
    bash é improvisado, Go é bom para produto, Python fica no meio do caminho, Ruby fica mais perto do bash, e Rust mais perto do Go
    Scripts são úteis para combinar rapidamente comandos do sistema operacional e resolver tarefas pontuais
    O Go não tem essa espontaneidade

    • Também concordo com essa ideia de que Python fica “no meio do caminho”
      No Debian, tentei rodar um app gtk simples com uv; as dependências estavam todas certas, mas ainda assim não funcionou e acabou em Core Dump
      Toda vez que tento usar Python de novo, algo assim acontece
      O Go é verboso, mas depois de compilar, simplesmente funciona
    • Tenho sensação parecida
      A questão central é conseguir terminar tudo em um único arquivo
      Dá para fazer um script de 500 linhas em Go, mas a linguagem já parte da premissa de múltiplos arquivos e módulos
      O fato de bang-line não funcionar também vem disso
      Se de qualquer forma go run cria um binário temporário, então me parece melhor simplesmente compilar e colocar em /usr/local/bin
    • Dizer que bash fica mais perto dos comandos do sistema é um equívoco
      bash também é apenas uma camada de abstração sobre o sistema, tanto quanto Python; só parece diferente por ser o shell padrão
    • Na era em que LLMs modificam código por você, talvez a legibilidade se torne mais importante do que a ergonomia de escrita
      Especialmente no sentido de tornar legível para humanos o código que a LLM gerou
  • Concordo que um usuário iniciante em Python não deveria precisar saber a diferença entre pip, poetry e uv
    Mas um blogueiro escrevendo sobre esse tema pelo menos deveria saber que o uv resolve esse problema
    Crítica ignorante não convence

    • Há a pergunta se o uv resolve para Python algo no estilo “write once, run anywhere” do Go
      Eu também não entendi completamente o conceito do uv, então fiquei curioso
  • Eu gosto de escrever scripts em Python
    Dá para fazer coisas rápido e resolver tarefas simples sem se preocupar com tipos ou memória
    Mas não quero usar isso em aplicações grandes

    • Eu também gosto de scripting em Python, mas odeio instalar script dos outros
    • Essa é uma visão centrada em Linux
      A maioria dos sistemas já vem com Python, e para scripts simples isso basta
      Pensando que seria preciso instalar Go, eu ainda acharia melhor usar Python com uv
      Como o próprio autor disse que “começou meio que trollando”, no fim a questão é só preferência por Go
    • Acho que JS também não é ruim como linguagem de script
      Com node bla.js já resolve
    • Você sempre precisa se preocupar com tipos
      É preciso saber o que a função retorna, e quando você conhece bem a linguagem, os tipos básicos viram memória muscular
      Isso vale também para linguagens com tipagem estática
    • Python é excelente para quem desenvolve, mas um pesadelo para distribuição e integração
      Se você está pensando nos outros, não deveria escrever código para distribuição em Python
  • Eu esperava uma crítica ao Python, mas no fim foi uma dica útil
    Se a linguagem usa // como comentário, dá para adaptar esse truque
    Funciona com C/C++, Java, JavaScript, Rust, Swift, Kotlin, ObjC, D, F#, GLSL etc.
    Em especial, é interessante para criar demos gráficas de arquivo único em GLSL
    Exemplo no Shadertoy
    Em C, também dá para usar comentário de bloco com algo como /*/../usr/bin/env gcc "$0" "$@"; ./a.out; rm -vf a.out; exit; */

    • Existe o projeto swift-sh, que permite executar scripts Swift com dependências externas
      É uma ideia parecida com o uv para Swift
      Swift também tem suporte oficial a shebang
    • Em C/C++, você pode simplesmente escrever #! diretamente
      Na época do TCC, eu usava isso como “scripting em C”
      Em projetos grandes, a estrutura era um script de build ler o manifesto, compilar e executar depois
      Mas controlar o ambiente é difícil, então isso não serve muito bem para uso profissional
    • Rust não precisa desse tipo de gambiarra
      Tem suporte direto a shebang
  • Se quiser uma linguagem com mais ergonomia, o .NET 10 também tem o recurso “run file directly”
    Ele suporta shebang e instala pacotes automaticamente dentro do script
    Com a diretiva #:sdk, dá até para subir um web app na hora

    • Hoje mesmo escrevi um script em C# com esse recurso pela primeira vez e a experiência foi bem boa
      Só que a compilação AOT ainda está meio crua
  • No começo achei que seria uma crítica ao Python, mas acabou me fazendo pensar sobre a direção dos ecossistemas de linguagem
    Acho que foi um grande erro o ML ter ficado preso ao Python
    Porque é lento, o sistema de tipos é desconfortável e a distribuição é difícil
    Agora é hora de considerar alternativas como TypeScript, Go e Rust

    • Concordo
      Mas a razão de ML ter escolhido Python foi a FFI baseada em C
      NodeJS, Rust e Go são fracos nisso
      Aí o Python leva vantagem
      O ideal seria uma linguagem tão simples quanto Python, mas com sistema de tipos e modelo de distribuição melhores
    • Não concordo com a ideia de substituir por TypeScript
      Não quero trocar Python por uma linguagem saída do ecossistema JS
    • ML foi para Python por pressão de mercado
      Lisp ou Lua (Torch) seriam opções melhores, mas Python venceu pela simplicidade
      Eu mesmo estou desenvolvendo um framework de ML baseado em Lisp, mas acho difícil conseguir adoção
    • O inferno de dependências do Python continua sério
      Problemas de compatibilidade de versão, ausência de semver, ecossistema instável e assim por diante fazem parecer que ele está atrás do JS
      JS/Node amadureceu nos últimos 10 anos, mas Python ainda parece preso em 2012
      É realmente uma pena que ML tenha se padronizado em Python
    • Eu quero uma linguagem simples e expressiva, mas com tipagem forte + compilação nativa
      Ao criar ferramentas CLI, Go é muito mais rápido que Python
      Acabei voltando para Python por causa da diferença de LOC, mas sinto falta do Go toda vez que executo
      Talvez OCaml fosse o ideal, mas o tooling antiquado pesa
  • O problema de scripts em Go é que a primeira linha não pode ter espaço
    Porque o gopls força formatação automática
    Como em CI também é importante manter consistência de formatação, isso importa na prática
    Mas o problema maior é que não dá para usar go.mod
    Ou seja, não dá para fixar versões de dependências, então a garantia de compatibilidade fica mais fraca

    • Ainda assim, versões major já ficam travadas pela rota de importação, então no básico a compatibilidade existe
    • Isso é um problema de compatibilidade de linguagem/runtime, não de dependências