- Khi đặt các script tự động hóa trong
~/bin/ để chạy, có thể dùng uv cùng metadata script nội tuyến để quản lý script Python gọn nhẹ hơn
- Nếu
~/bin đã có trong PATH, có thể đặt shebang là #!/usr/bin/env -S uv run --script để chạy tên file như một lệnh
- Cấu hình này khiến shell chạy
uv run --script trước, rồi truyền file script hiện tại làm đối số
- uv sẽ bỏ qua dòng shebang, sau đó thực thi phần còn lại như một file Python thông thường
- Sau khi cấp quyền thực thi bằng
chmod a+x {filname}, có thể dùng nó như một script dòng lệnh Python đơn giản với khả năng tự động xử lý phụ thuộc
Chạy uv bằng shebang
- Tạo và dùng nhiều script trong thư mục
~/bin/ để tự động hóa công việc
- Nếu
~/bin nằm trong PATH, có thể chạy trực tiếp script chỉ bằng tên file
- Shebang dùng trong trường hợp này như sau
#!/usr/bin/env -S uv run --script
Luồng thực thi và thiết lập quyền
- Shebang trên sẽ chạy
uv run --script từ dòng lệnh và truyền file script làm đối số
- uv sẽ bỏ qua shebang rồi thực thi phần còn lại của file như mã Python thông thường
- Trước khi chạy, cần cấp quyền thực thi cho script
chmod a+x {filname}
- Sau khi cấu hình xong, có thể chạy ngay một script dòng lệnh Python đơn giản có khả năng tự động xử lý phụ thuộc
1 bình luận
Các ý kiến trên Hacker News
Hôm nay tôi mới biết đến env -S, và ngay khi thấy shebang trong bài viết, tôi đã nghĩ “trên Linux chắc không chạy đâu, vì shebang chỉ có thể truyền một đối số”
Ví dụ, khi chạy
foo.pybắt đầu bằng#!/usr/bin/env -S uv run --script, hệ điều hành thực ra sẽ truyền toàn bộ dòng shebang như một đối số, kiểu/usr/bin/env '-S uv run --script' foo.pyNhưng tùy chọn
-Scủaenvlại tách chuỗi đó thành nhiều đối số, nên nó hoạt động. Rất hay và hữu íchĐặt
#!/usr/bin/guile \rồi dòng tiếp theo là-e main -s, và đóng bằng!#thì nó sẽ thành kiểu/usr/bin/guile -e main -s filenameTôi tò mò vì sao họ lại phải làm như vậy. Có lẽ
env -Slà tính năng mới được thêm gần đây, hoặc không dùng được trên mọi nền tảng mà họ quan tâmhttps://www.gnu.org/software/guile/manual/html_node/The-Meta-Switch.html
makenhưng dùng YAML để cấu hìnhĐặt
#!/usr/bin/env -s go run github.com/rliebz/tusk@latest -flàm shebang, rồi dùng gosh, một Go shell, làm trình thông dịch kiểuinterpreter: go run mvdan.cc/sh/v3/cmd/gosh@latest -sNhư vậy, chỉ cần có Go được cài đặt là sẽ có một CLI chạy được trên mọi kiến trúc
Ví dụ nix-shell cho phép đặt
#!/usr/bin/env nix-shell, rồi dòng tiếp theo là#!nix-shell --pure -i runghc ./default.nixđể chỉ định dependency và đối số, sau đó có thể viết tiếp mã HaskellQuy tắc tách theo khoảng trắng kỳ lạ của dòng shebang là một lỗi cũ đã hóa thành khuyết điểm không thể sửa của Unix
Mỗi lần dùng những mẹo kiểu này, tôi lại nghĩ rằng nếu hồi thập niên 1980 ai đó bỏ ra chỉ 30 phút thì đã tránh được nhiều năm bực bội về sau. Dòng shebang đáng lẽ phải được tách như
/bin/shngay từ đầuKhông phải shebang, nhưng gần đây tôi đang dùng uv như một công cụ cài đặt
Vì việc đóng gói và phân phối các công cụ Python CLI có dependency phức tạp khá khó, tôi đã tận dụng nó theo hai cách
Một cách là sao chép script cài đặt
curl | shrồi thêmuv tool install --python python3.12 my-toolở cuối để người dùng có thể cài CLI bằng một dòng lệnh; cách còn lại là tạo một gói “trình cài đặt” nhỏ trên PyPI chỉ phụ thuộc vàouv, để nó chạyuv tool installCả hai cách đều cài công cụ CLI, Python 3.12 và toàn bộ dependency Python vào một môi trường cô lập. Người dùng không cần quản lý môi trường ảo, và với cách
curl | shthì thậm chí không cần cài sẵn PythonTừ đó, số issue trên GitHub do người dùng làm hỏng các dependency phức tạp đã giảm đi rất nhiều
Chi tiết và liên kết mã nguồn: https://aider.chat/2025/01/15/uv.html
openssl-dev,gzip,ffi, và phải biên dịch Python chăngÍt nhất khi dùng ASDF-VM, tôi từng gặp trải nghiệm như vậy vì công cụ cài Python khác bên trong nó
misevàaquacũng có khái niệm cài đặt công cụ hữu íchNhân lúc đang có câu chuyện thú vị về shebang, tôi đã làm thứ này để chạy các file Prolog
.pltừ shell/*usr/bin/env scryer-prolog "$0" "$@" ; exit #*/Nếu là một ngôn ngữ script có chú thích
/**/nhưng không hỗ trợ trực tiếp chú thích#hay shebang#!thì nhìn chung có lẽ sẽ chạy đượcBình luận gốc: https://github.com/mthom/scryer-prolog/issues/2170#issuecomment-1821713993
File này vừa là một file shell hợp lệ, vừa là một file Prolog hợp lệ. Khi chạy bằng shell, dòng đầu tiên tìm glob pattern
/*usr/bin/envvà chạy/usr/bin/env, rồienvchạyscryer-prolog "test.pl". Về phía Prolog, dòng đầu tiên là chú thích/* ... */nên bị bỏ quaSau đó shell chạy
exitsau dấu;để không diễn giải phần mã Prolog còn lại như shell, còn#khiến shell bỏ qua phần kết thúc chú thích Prolog*/, tránh in lỗiCó lẽ đây vừa là thứ hay nhất vừa là thứ tệ nhất tôi từng tạo ra
Thêm vào đó, chương trình C cũng có thể được làm cho chạy được bằng cách dùng dòng đầu dạng
//usr/bin/env sh -c .... Khi chạy, nó build bằngmakerồi chạy binary kết quả, nên nếu sửa source rồi chạy lại thì output đã thay đổi sẽ hiện ra ngaySau
#!/bin/sh, có thể đặt dòng# the next line restarts using tcl \rồi cho chạyexec tclsh "$0" "$@"Cách này tận dụng việc trong TCL ngay cả dòng chú thích cũng có thể nối dòng
Bài Lazy self-installing Python scripts with uv của Trey Hunner có nội dung chi tiết hơn kèm ví dụ
https://treyhunner.com/2024/12/lazy-self-installing-python-scripts-with-uv/
printf %.1s 0{1..80} $'\n'hoặcfor i in {1..80}; do echo -n 0; done; echoVí dụ ffmpeg thì tôi cũng không rõ khác gì so với
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.mp4Thành thật mà nói phía Python trông còn phức tạp hơn
Với các tác vụ nhỏ, có thể dùng mẹo này cùng mise(mise-en-place): https://mise.jdx.dev/tasks/toml-tasks.html#shell-shebang
Cách làm là đặt
uv = 'latest'trongtools, rồi trong blockruncủa tác vụ khai báo#!/usr/bin/env -S uv run --scriptcùngdependencies = ["requests<3", "rich"], sau đó đưa mã Python vào16 ngày trước cũng đã có bài liên quan và thảo luận: Uv's killer feature is making ad-hoc environments easy https://news.ycombinator.com/item?id=42676432 (502 điểm, 417 bình luận)
Đây là một mẹo đáng ghi nhớ
Gần đây tôi đã đổi alias Python thành
uv run pythonvà thấy khá dễ chịu. Không cần quản lý.venvv.v., cũng không phải bận tâm chuyện cài đặt hệ thống hay cài đặt global/user; trên Debian thì đặc biệt hữu íchKhi chạy REPL trong một project hay môi trường, cũng có thể gọi mà không cần
activate, nên bớt phải suy nghĩ hơnTuy nhiên, khi gọi trực tiếp
.pybằng uv, nhược điểm là đường dẫn file project/môi trường được xác định theo thư mục làm việc hiện tại, chứ không theo vị trí của file.pyCó switch tường minh
--projectnên có thể dùng kiểuuv run --project /script.py, nhưng nếu có một switch project tương đối theo file đích thì sẽ tốt hơn vì giảm được sự lặp lạiKhi dùng
uvhayuvxlàm trình chạy lệnh, tôi đã gặp vài cạm bẫy, nhưng phần lớn thời gian nó hoạt động tốt, và những lúc đó tiết kiệm được rất nhiều thời gianLà một lập trình viên Python và cũng là người hay tò mò, thư mục home của tôi đầy các bản clone nông và thư mục thử nghiệm dùng một lần; việc nó giúp giảm gánh nặng quản lý như vậy thật sự rất hữu ích
Tôi muốn “mượn” ý tưởng này để dùng. Tôi tò mò không biết họ dùng một phần mở rộng riêng không phải
.py, hay chỉ dựa vào việc file.pycó bật bit thực thi và có shebang để coi đó là tín hiệu rằng file có thể chạy trực tiếp như một script, thay vì qua Python của hệ thốngKhông phải là 0, nhưng tôi vẫn đang khá tích cực tìm thêm. Nếu biết bạn đã gặp cạm bẫy nào thì tôi muốn thêm vào danh sách
.pykèm bit thực thi và dòng shebang như trong bài viếtVới Pipenv, từng có vấn đề khi một dependency cụ thể có wheel khác nhau theo từng nền tảng, còn Poetry thì hoạt động tốt
File khóa của Pipenv chỉ bao gồm dependency của nền tảng đã khóa, trong khi Poetry thêm mọi biến thể nền tảng vào file khóa
Tôi vẫn chưa tìm thấy tài liệu nói uv xử lý trường hợp này như thế nào
Ý chính là như sau
Một PEP được công bố gần đây định nghĩa cách script Python khai báo dependency trong phần chú thích mở đầu, và uv là một trình chạy script Python kiêm trình quản lý gói: nó quét các dependency đó, đáp ứng chúng, rồi chạy script trong trạng thái có thể import các module tương ứng
Nếu ở shebang, tức dòng chú thích đầu tiên của script Python, ta gọi file bằng
uvthay vìpython, thì khi “chạy” script, hiệu ứng này sẽ tự động có đượcKết quả là ngay cả các script Python có dependency cần cấu hình cũng cứ thế chạy được
$PATHVà dù uv sẽ cài dependency, việc cách này có tự động cài luôn cả trình thông dịch Python hay không thì chưa rõ
Cá nhân tôi không thấy cách này tiết kiệm thời gian hơn so với cách cũ là tạo môi trường rồi gọi nó
Tôi cũng không rõ uv có thể nạp môi trường trong phiên tương tác hay chỉ được dùng để chạy script
Nếu bạn thích TypeScript thì cũng có thể dùng cách tương tự với Deno. Bun có lẽ cũng được, nhưng tôi chưa tự thử
Viết như
#! /usr/bin/env -S deno run, và nếu cần cờ quyền thì thêm như#! /usr/bin/env -S deno run --allow-env --allow-read --allow-net