- Ao usar uv e metadados de script inline no fluxo de manter e executar scripts de automação em
~/bin/, é possível lidar com scripts Python de forma mais leve
- Se
~/bin estiver no PATH, você pode definir o shebang como #!/usr/bin/env -S uv run --script e executar o arquivo pelo nome, como se fosse um comando
- Essa configuração faz com que o shell execute primeiro
uv run --script e passe o arquivo de script atual como argumento
- O uv ignora a linha shebang e executa o restante do conteúdo como um arquivo Python comum
- Ao conceder permissão de execução com
chmod a+x {filname}, ele pode ser usado como um script simples de linha de comando em Python que trata dependências automaticamente
Executando uv como shebang
- Uso vários scripts no diretório
~/bin/ para automatizar tarefas
- Se
~/bin estiver incluído no PATH, é possível executar scripts diretamente apenas pelo nome do arquivo
- O shebang a ser usado nesse caso é o seguinte
#!/usr/bin/env -S uv run --script
Fluxo de execução e configuração de permissões
- O shebang acima executa
uv run --script na linha de comando e passa o arquivo de script como argumento
- O uv ignora o shebang e executa o restante do arquivo como código Python comum
- Antes de executar, é preciso conceder permissão de execução ao script
chmod a+x {filname}
- Depois de concluir a configuração, você pode executar diretamente um script simples de linha de comando em Python que trata dependências automaticamente
1 comentários
Opiniões do Hacker News
Hoje conheci env -S pela primeira vez e, assim que vi o shebang no artigo, pensei: “isso não deve funcionar no Linux, já que o shebang só consegue passar um único argumento”
Por exemplo, ao executar um
foo.pyque começa com#!/usr/bin/env -S uv run --script, o sistema operacional na prática passa a linha inteira do shebang como um único argumento, como em/usr/bin/env '-S uv run --script' foo.pySó que o
-Sdoenvdivide essa string novamente em vários argumentos, então funciona. Muito bacana e útilColocando
#!/usr/bin/guile \e, na linha seguinte,-e main -s, fechando com!#, fica algo como/usr/bin/guile -e main -s filenameFico curioso sobre por que fizeram justamente assim. Talvez o
env -Stenha sido adicionado recentemente, ou não estivesse disponível em todas as plataformas que interessavamhttps://www.gnu.org/software/guile/manual/html_node/The-Meta-Switch.html
make, mas que usa YAML para configuraçãoColocando
#!/usr/bin/env -s go run github.com/rliebz/tusk@latest -fcomo shebang, e usando o gosh, um shell em Go, como interpretador com algo comointerpreter: go run mvdan.cc/sh/v3/cmd/gosh@latest -sAssim, se Go estiver instalado, vira uma CLI executável em qualquer arquitetura
Por exemplo, o nix-shell permite colocar
#!/usr/bin/env nix-shelle, na linha seguinte,#!nix-shell --pure -i runghc ./default.nix, para especificar dependências e argumentos, e depois continuar com o código HaskellAs regras estranhas de separação por espaços na linha de shebang são um bug antigo que acabou virando uma falha impossível de corrigir no Unix
Toda vez que vejo gambiarras como essas, penso que, se tivessem gasto 30 minutos nos anos 1980, teriam evitado anos de irritação depois. A linha de shebang deveria ter sido separada como
/bin/shdesde o inícioNão é shebang, mas ultimamente tenho usado o uv como ferramenta de instalação
Como é difícil empacotar e distribuir ferramentas CLI em Python com dependências complexas, usei isso de duas formas
Uma foi copiar um script de instalação
curl | she adicionaruv tool install --python python3.12 my-toolno final, para permitir que o usuário instale a CLI com uma única linha; a outra foi criar um pequeno pacote “instalador” no PyPI que tem apenasuvcomo dependência e faz executaruv tool installNos dois casos, a ferramenta CLI, o Python 3.12 e todas as dependências Python são instalados em um ambiente isolado. O usuário não precisa gerenciar ambientes virtuais e, no método
curl | sh, nem precisa ter Python instaladoDesde então, os issues no GitHub causados por usuários quebrando dependências complexas diminuíram bastante
Mais detalhes e links para o código: https://aider.chat/2025/01/15/uv.html
openssl-dev,gzipeffi, e que seja necessário compilar o PythonPelo menos quando usei ASDF-VM, tive essa experiência por causa de outras ferramentas de instalação de Python usadas por ele
miseeaquatambém têm um conceito útil de instalação de ferramentasJá que surgiu essa história interessante de shebang, fiz algo assim para executar arquivos Prolog
.plno shell/*usr/bin/env scryer-prolog "$0" "$@" ; exit #*/Se for uma linguagem de script que tenha comentários
/**/, mas não ofereça suporte direto a comentários#ou ao shebang#!, em geral acho que deve funcionarComentário original: https://github.com/mthom/scryer-prolog/issues/2170#issuecomment-1821713993
Esse arquivo é ao mesmo tempo um arquivo de shell válido e um arquivo Prolog válido. Quando executado pelo shell, a primeira linha procura o padrão glob
/*usr/bin/env, executa/usr/bin/env, e oenvexecutascryer-prolog "test.pl". Do lado do Prolog, a primeira linha é ignorada por ser um comentário/* ... */Depois disso, o shell executa o
exitapós o;, para não interpretar o restante do código Prolog como shell, e o#faz o shell ignorar o terminador de comentário do Prolog,*/, evitando mensagens de erroTalvez seja a melhor e a pior coisa que já fiz
Além disso, também dá para tornar um programa em C executável usando uma primeira linha no formato
//usr/bin/env sh -c .... Ao executar, ele compila commakee depois executa o binário resultante; então, se você modificar o fonte e executar de novo, a saída alterada aparece imediatamenteDá para colocar
#!/bin/sh, depois na linha seguinte# the next line restarts using tcl \, e fazer executarexec tclsh "$0" "$@"Isso aproveita o fato de que, em TCL, até linhas de comentário podem ter continuação de linha
O artigo de Trey Hunner, Lazy self-installing Python scripts with uv, traz mais detalhes com exemplos
https://treyhunner.com/2024/12/lazy-self-installing-python-scripts-with-uv/
printf %.1s 0{1..80} $'\n'oufor i in {1..80}; do echo -n 0; done; echoTambém não entendo muito bem em que o exemplo com ffmpeg é diferente de
ffmpeg -i in.mp4 -c:v copy -filter:a volumedetect -pass 1 -f null /dev/null && ffmpeg -i in.mp4 -c:v copy -filter:a "loudnorm" -pass 2 out.mp4Sinceramente, a versão em Python parece mais complicada
Para tarefas pequenas, dá para usar esse truque junto com mise (mise-en-place): https://mise.jdx.dev/tasks/toml-tasks.html#shell-shebang
A ideia é colocar
uv = 'latest'emtools, declarar#!/usr/bin/env -S uv run --scriptedependencies = ["requests<3", "rich"]no blocorunda tarefa, e depois inserir o código PythonHá 16 dias também houve um artigo e discussão relacionados: Uv's killer feature is making ad-hoc environments easy https://news.ycombinator.com/item?id=42676432 (502 pontos, 417 comentários)
É um truque que vale a pena lembrar
Recentemente troquei meu alias de Python para
uv run pythone tem sido bem agradável. Não preciso gerenciar.venvetc., nem me preocupar com instalação no sistema ou instalações globais/de usuário; no Debian, isso ajuda especialmenteMesmo ao executar o REPL dentro de um projeto ou ambiente, dá para chamar sem
activate, o que reduz o que preciso ter em mentePorém, ao chamar um
.pydiretamente com uv, a desvantagem é que os caminhos dos arquivos de projeto/ambiente são resolvidos com base no diretório de trabalho atual, não com base na localização do arquivo.pyExiste a opção explícita
--project, então dá para usar algo comouv run --project /script.py, mas seria bom ter uma opção de projeto relativa ao arquivo alvo para reduzir a repetiçãoAo usar
uvouuvxcomo executores de comandos, já esbarrei em algumas armadilhas, mas na maior parte do tempo funciona bem e, quando funciona, economiza bastante tempoComo desenvolvedor Python e pessoa curiosa, meu diretório home vive cheio de clones rasos e diretórios de testes descartáveis; é realmente útil reduzir esse peso de gerenciamento
Quero roubar essa ideia. Fico curioso se usam uma extensão separada em vez de
.py, ou se apenas ter o bit de execução ativado em um arquivo.pye uma shebang serve como sinal de que ele pode ser executado diretamente como script, e não pelo Python do sistemaNão é zero, mas estou procurando com bastante afinco. Se eu puder saber quais armadilhas você encontrou, gostaria de adicioná-las à lista
.pycom o bit de execução e a linha shebang mostrada no artigoNo Pipenv, havia problemas quando uma dependência específica tinha wheels diferentes por plataforma; no Poetry isso funcionava bem
O arquivo de lock do Pipenv incluía apenas as dependências da plataforma em que o lock foi feito, enquanto o Poetry adicionava todas as variações de plataforma ao arquivo de lock
Ainda não encontrei documentação sobre como o uv se comporta nesse caso
O ponto principal é este
Um PEP publicado recentemente define uma forma de scripts Python declararem dependências nos comentários iniciais, e o uv é tanto um executor de scripts Python quanto um gerenciador de pacotes que escaneia essas dependências, as satisfaz e então executa o script em um estado em que esses módulos podem ser importados
Se, na shebang, o comentário da primeira linha de um script Python, você fizer o arquivo ser chamado com
uvem vez depython, obtém automaticamente esse efeito ao “executar” o scriptComo resultado, até scripts Python com dependências que exigem configuração simplesmente rodam
$PATHE, embora o uv instale as dependências, não está claro se ele também instala automaticamente o interpretador Python dessa forma
Pessoalmente, não me parece economizar tempo em relação ao jeito antigo de criar um ambiente e chamá-lo
Também não tenho certeza se o uv consegue carregar o ambiente apenas em sessões interativas ou se é usado somente para execução de scripts
Se você gosta de TypeScript, dá para usar a mesma abordagem com Deno. Bun também deve funcionar, mas não testei pessoalmente
Use algo como
#! /usr/bin/env -S deno run; se precisar de flags de permissão, adicione-as assim:#! /usr/bin/env -S deno run --allow-env --allow-read --allow-net