How do you handle packages? I want scripts to a be a single file with a shebang, not a repo with a requirements.txt that I need to run in a venv. To me, this is the biggest blocker to using Python for any non-trivial scripting (which is precisely the kind where I wouldn't want to use bash), but I'd like to know how others deal with it.<p>C# scripts let you reference packages in a comment at the top of the file, for example:<p><a href="https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/" rel="nofollow">https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-...</a>
You can specify requirements at the top of file and uv can run the script after automatically installing the dependencies.<p><a href="https://avilpage.com/2025/04/learn-python-uv-in-100-seconds.html" rel="nofollow">https://avilpage.com/2025/04/learn-python-uv-in-100-seconds....</a>
This works really well in my experience, but it does mean you need to have a working internet connection the first time you run the script.<p><pre><code> # /// script
# dependencies = [
# "cowsay",
# ]
# ///
import cowsay
cowsay.cow("Hello World")
</code></pre>
Then:<p><pre><code> uv run cowscript.py
</code></pre>
It manages a disposable hidden virtual environment automatically, via a very fast symlink-based caching mechanism.<p>You can also add a shebang line so you can execute it directly:<p><pre><code> #!/usr/bin/env -S uv run --script
#
# /// script
# dependencies = ["cowsay"]
# ///
import cowsay
cowsay.cow("Hello World")
</code></pre>
Then:<p><pre><code> chmod 755 cowscript
./cowscript</code></pre>
As someone who writes a lot of python, I love uv, but isn't on nearly every system like python is, which is one of the arguments for using python here in the first place
Nix allows you to do this with any language and required dependency:
<a href="https://wiki.nixos.org/wiki/Nix-shell_shebang" rel="nofollow">https://wiki.nixos.org/wiki/Nix-shell_shebang</a>
What can you do with bash that isn't in the stdlib of python?<p>Generally the only nontrivial scripting I ever do is associated with a larger project, so I often already have a pyproject.toml and a UV environment, and I just add the dependencies to the dev group.
Well, that's kind of what I mean. For scripts in a python project, you can freely use whatever packages you need. But for one-off scripts, if you need bs4 or something, you're screwed. Either your script now has external dependencies or it requires special tooling.<p>It just feels strange that C# of all languages is now a better scripting tool than Python, at least out of the box. I did notice uv has exactly the feature I'm looking for, though it's obviously third-party:<p><a href="https://docs.astral.sh/uv/guides/scripts/#declaring-script-dependencies" rel="nofollow">https://docs.astral.sh/uv/guides/scripts/#declaring-script-d...</a><p>Is everyone just using uv now instead of pip, perhaps? Or is just another alongside pipenv, conda, poetry, etc.? (Python's not my main these days, so I'm out of the loop.)
I don't understand. To return to GP's point, what can you do in bash that you can't do in Python? Said in another way, what does bash offer that you would need to tackle with a dependency in Python? My understanding is that there is no such thing, and accordingly, you can still end up with something that is better than bash if you just use Python and call out to other tools with subprocess.
There's bash. Then you need better loops/conditionals and more usable structures. That's when one should think of using a scripting language instead. I think the parent goes too far after that and what he's talking about is not something bash can do (well).<p>That said, a lot of very complicated things are actually written in bash. Distrobox I think is for example.
UV is taking over really fast, it seems to be much more popular any other option.<p>I suspect conda still has some market share too but I've never needed it.
<a href="https://peps.python.org/pep-0723/" rel="nofollow">https://peps.python.org/pep-0723/</a>, e.g.:<p><pre><code> # /// script
# requires-python = ">=3.11"
# dependencies = [
# "requests<3",
# "rich",
# ]
# ///
import requests
from rich.pretty import pprint
resp = requests.get("https://peps.python.org/api/peps.json")
data = resp.json()
pprint([(k, v["title"]) for k, v in data.items()][:10])</code></pre>
what can you do with bash that you cannot do with the python standard library and shelling out?
> How do you handle packages?<p>The same way you handle them with bash?<p>Install them?<p>What are we talking about here?
Odd, I don't see any mention of subprocess.run, the workhorse of python scripting.<p>Quick rundown for the unfamiliar:<p>Give it a command as a list of strings (e.g., subprocess.run(["echo", "foo"]).)<p>It takes a bunch of flags, but the most useful (but not immediately obvious) ones are:<p><pre><code> check=True: Raise an error if the command fails
capture_output=True: Captures stdout/stderr on the CompletedProcess
text=True: Automatically convert the stdout/stderr bytes to strings
</code></pre>
By default, subprocess.run will print the stdout/stderr to the script's output (like bash, basically), so I only bother with capture_output if I need information in the output for a later step.
Xonsh is perfect for this: <a href="https://xon.sh" rel="nofollow">https://xon.sh</a>
The Python stdlib does not get enough credit. People complain about things like how its http client is dated and slow, but its pretty amazing that it’s just right there if you need it, no external dependencies needed. And it’s sitting right next to difflib, graphlib, pathlib, struct, glob, tkinter, and dozens of others. Sure, every one of these is limited individually, but those limitations are stable and well understood!
Dupe: <a href="https://news.ycombinator.com/item?id=46176113">https://news.ycombinator.com/item?id=46176113</a>
Weren't we already?
Pretty much anything longer then a throwaway one liner I write in python.<p>Would be cool if python had a pipe operator though.<p>The back ticks in ruby is pretty ergonomic too. Wish python had a simpler way to run commands. Kind of tedious to look up subprocess run arguments and also break things up into arrays.