[HN Gopher] Using Python for Scripting
       ___________________________________________________________________
        
       Using Python for Scripting
        
       Author : birdculture
       Score  : 155 points
       Date   : 2025-12-08 08:52 UTC (6 days ago)
        
 (HTM) web link (hypirion.com)
 (TXT) w3m dump (hypirion.com)
        
       | zahlman wrote:
       | Dupe: https://news.ycombinator.com/item?id=46176113
        
         | froh wrote:
         | this discussion took off, however...
        
       | mubou2 wrote:
       | 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.
       | 
       | C# scripts let you reference packages in a comment at the top of
       | the file, for example:
       | 
       | https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-...
        
         | eternityforest wrote:
         | What can you do with bash that isn't in the stdlib of python?
         | 
         | 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.
        
           | mubou2 wrote:
           | 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.
           | 
           | 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:
           | 
           | https://docs.astral.sh/uv/guides/scripts/#declaring-
           | script-d...
           | 
           | 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.)
        
             | eternityforest wrote:
             | UV is taking over really fast, it seems to be much more
             | popular any other option.
             | 
             | I suspect conda still has some market share too but I've
             | never needed it.
        
               | psunavy03 wrote:
               | It's amazing what happens when something just works.
        
             | jollyllama wrote:
             | 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.
        
               | dizhn wrote:
               | 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).
               | 
               | That said, a lot of very complicated things are actually
               | written in bash. Distrobox I think is for example.
        
               | DonHopkins wrote:
               | >That said, a lot of very complicated things are actually
               | written in bash. Distrobox I think is for example.
               | 
               | They're only complicated BECAUSE they're written in bash.
               | If they were written in Python they would be much less
               | complicated, more modular, able to use many existing
               | industrial strength well tested python modules instead of
               | rolling their own ad-hoc ones, and much easier to
               | maintain.
        
         | chillaranand wrote:
         | You can specify requirements at the top of file and uv can run
         | the script after automatically installing the dependencies.
         | 
         | https://avilpage.com/2025/04/learn-python-uv-in-100-seconds....
        
           | simonw wrote:
           | 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.                 # /// script       #
           | dependencies = [       #     "cowsay",       # ]       # ///
           | import cowsay       cowsay.cow("Hello World")
           | 
           | Then:                 uv run cowscript.py
           | 
           | It manages a disposable hidden virtual environment
           | automatically, via a very fast symlink-based caching
           | mechanism.
           | 
           | You can also add a shebang line so you can execute it
           | directly:                 #!/usr/bin/env -S uv run --script
           | #       # /// script       # dependencies = ["cowsay"]
           | # ///       import cowsay       cowsay.cow("Hello World")
           | 
           | Then:                 chmod 755 cowscript       ./cowscript
        
             | emidln wrote:
             | I wish env -S was more portable. It's a newer feature of
             | the coreutils env implementation and isn't supported
             | elsewhere afaik.
        
               | collinfunk wrote:
               | FreeBSD 6.0 added 'env -S'. They have adopted a few
               | different GNU inspired options recently, which I am happy
               | about.
        
               | networked wrote:
               | You can use so-called "exec magic" instead of `env -S`.
               | Here is an explanation with Python examples:
               | https://dbohdan.com/scripts-with-dependencies#exec-magic
               | (disclosure: my site). In short:                 #!
               | /bin/sh       "exec" "/usr/bin/env" "uv" "run" "--quiet"
               | "--script" "$0" "$@"       # /// script       #
               | dependencies = [       #   "cowsay",       # ]       #
               | ///       import cowsay       cowsay.cow("Hello, world!")
               | 
               | On systems that can't run uv, like NetBSD and OpenBSD,
               | switch to pipx:                 #! /bin/sh       "exec"
               | "/usr/bin/env" "pipx" "run" "$0" "$@"       # ...
        
             | vovavili wrote:
             | If not having a working internet connection is a concern, I
             | would package your script into a shiv zipapp -
             | https://shiv.readthedocs.io/en/latest/
        
           | presbyterian wrote:
           | 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
        
             | ewidar wrote:
             | Sure but the sub question here was about packages.
             | 
             | If you are installing packages, then starting with
             | installing uv should be fine.
        
             | vovavili wrote:
             | The catch is that there could be a version mismatch between
             | the version of Python installed on the end user's computer
             | and the version on which the script was developed. This
             | problem can be solved with uv, and there aren't really
             | Python-native ways available.
        
         | Rucadi wrote:
         | Nix allows you to do this with any language and required
         | dependency: https://wiki.nixos.org/wiki/Nix-shell_shebang
        
           | albertoCaroM wrote:
           | Whoa! This is a revelation. I already loved Nix and used nix-
           | shell extensively, but this is the missing piece: fully
           | reproducible Python scripts without compromise.
        
             | luz666 wrote:
             | Or install direnv and put your dependencies into a
             | shell.nix, setup the bash hook according the manual, create
             | the .envrc with the content "use nix". Then type "direnv
             | allow".
             | 
             | Then you can do e.g. use other persons python scripts
             | without modifying their shebang.
        
           | HumanOstrich wrote:
           | Now you have to set up Nix first and deal with that nightmare
           | of a learning curve. Just to auto-install some dependencies
           | for a script.
           | 
           | Might as well rewrite all your scripts in Rust too while
           | you're layering on unholy amounts of complexity.
        
             | Diti wrote:
             | It's like Vim, you learn it once, and you keep using it
             | forever once you're used to it.
             | 
             | I'm so thankful to see a flake.nix file in every single
             | cool project on code forges.
        
               | HumanOstrich wrote:
               | Yea that's a common theme of excuses for both Rust and
               | Nix. Wrong though, because most anyone who can use a
               | computer at all can learn the basics of Vim.
               | 
               | Seeing that flake.nix badge of complexity lets me know a
               | project will be a nightmare to set up and will break
               | every other week. It's usually right next to the
               | Cargo.toml badge with 400 dependencies underneath.
        
               | kokada wrote:
               | Nix with Flakes never randomly break, I still have
               | projects from 3 or 4 years ago that I can still run `nix
               | build` and getting it running. Yes, if you try to update
               | the `flake.lock` this may introduce breakages, but this
               | is expected if you're pining `nixos-unstable` instead of
               | a stable branch.
        
               | Rucadi wrote:
               | To be honest I don't know what to say, you can use nix in
               | many ways, and you don't even require to know the
               | language.
               | 
               | The easiest entry-point is to just use it like a package
               | manager, you install nix (which is just a command...) and
               | then you have available the whole set of packages which
               | are searchable from here:
               | https://search.nixos.org/packages
               | 
               | nix-shell is just to download&add programs temporary to
               | your PATH.
               | 
               | I don't feel that this is harder than something like
               | "sudo apt install -y xxxxx" but for sure more robust and
               | portable, and doesn't require sudo.
               | 
               | If at some point you want to learn the language in order
               | to create configurations or packaging software, it may
               | require to check a lot more documentation and examples,
               | but for this I think it's pretty straightforward and is
               | not harder than any other package manager like aptitude,
               | homebrew or pacman.
        
               | Diti wrote:
               | I'm not sure what you mean by "a nightmare to set up".
               | You install Nix on your current OS with the
               | determinate.systems installer, and you enter `nix run
               | github:johndoe/project-containing-a-flake-dot-nix-file`
               | to try out the project and have the full reproducible
               | build taken care of by Nix.
               | 
               | Sure, installing packages the proper way requires a
               | little bit more setup (Home Manager, most likely, and
               | understanding where is the list of packages and which
               | command to build to switch configuration), but as trivial
               | as other complex tasks most of us hackers are capable of
               | doing (like using `jq` or Vim).
        
         | milliams wrote:
         | https://peps.python.org/pep-0723/, e.g.:                 # ///
         | 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])
        
         | paulddraper wrote:
         | > How do you handle packages?
         | 
         | The same way you handle them with bash?
         | 
         | Install them?
         | 
         | What are we talking about here?
        
           | fridder wrote:
           | I think it is the fact that python packaging has been
           | problematic for some time. setuptools/easy_install, pip/pipx,
           | poetry, conda, uv all promise they will be the thing that
           | "fixes" it
        
             | paulddraper wrote:
             | What does Bash use for its packaging?
             | 
             | Use that.
        
               | bccdee wrote:
               | Ok tbh bash uses the system package manager to install
               | command-line utilities, and using the system package
               | manager for python packages HAS been tried, and the
               | parallel use of system packaging and independent package
               | managers is part of why python package distribution is
               | such a mess.
        
               | skydhash wrote:
               | Why using independent package managers alongside the
               | system one? I think the introduction of non system
               | packagers is what brought us the whole mess we are in.
               | Most system packagers allows for custom repositories.
        
               | bccdee wrote:
               | Because the system package repository doesn't package
               | everything & isn't always up to date. And if you
               | introduce other repos to fix this, then you have an
               | ecosystem with multiple instances of the same package
               | distributed at different versions in different
               | repositories, and God help you if you need multiple
               | versions of one package, or if you need a different
               | version of Python entirely. Nix could do it, but not
               | anything else.
               | 
               | No--system python is for the system's scripts, and user
               | projects should have their dependencies sandboxed in a
               | virtual environment. That's the only model that really
               | works.
        
             | bigstrat2003 wrote:
             | Eh. Python packaging is just fine. Set up a venv, install
             | with pip, done. I think that the difficulty of installing
             | Python packages is wildly overblown on this site.
        
               | bdangubic wrote:
               | not "AI Bubble" overblown but close
        
         | a-dub wrote:
         | what can you do with bash that you cannot do with the python
         | standard library and shelling out?
        
         | kjkjadksj wrote:
         | What is wrong with writing a short wrapper in bash to activate
         | a conda env before running the script? Too unsexy?
        
         | DonHopkins wrote:
         | Isn't the fact that bash doesn't have a wonderful ecosystem of
         | reusable modules a much more enormous insurmountable problem
         | than the fact that you have to install python modules? You're
         | really missing the forest for the trees.
         | 
         | Not only does bash not have a module system like python, or a
         | vast ecosystem of modules like python, but also that it's much
         | too weak and brittle a language to implement most of those
         | modules that Python has, and can't even call native code
         | libraries directly.
         | 
         | Even with just its standard built in "batteries included"
         | libraries and no extension modules or native code modules,
         | Python is still much more powerful than bash and easier to code
         | and maintain.
         | 
         | If you're complaining about having to install Python modules,
         | you're usually already doing something that's impossible or
         | incredibly difficult to do in bash anyway.
         | 
         | Even something as simple and essential as fetching files via
         | http or parsing json. Bash has to call out to other programs to
         | do that, but you have to install those programs too, and while
         | Python can certainly call out to curl or wget or jq, it doesn't
         | have to, since it has all that and more built in.
         | 
         | There really is no comparison, because bash loses along so many
         | dimensions at once compared to Python.
        
         | froh wrote:
         | Isn't pipx addressing exactly that?
         | 
         | once the script is non-trivial, 'install' it using pipx, in
         | editable mode when you work on the script and as normal pipx
         | installed cli utility otherwise.
         | 
         | the venv part is then completely under the hood.
        
         | lgas wrote:
         | I don't have much need of this personally, but I was playing
         | around with an example from earlier in the thread and ended up
         | with this:                   #!/usr/bin/env -S uv run --with sh
         | --script         from sh import ifconfig
         | print(ifconfig("en0"))
         | 
         | which is a pretty nice experience assuming you already have
         | `uv` in the target environment.
        
         | bigstrat2003 wrote:
         | Honestly, I just use a requirements.txt file. It's not as
         | convenient as having a single file, it's true, but that is
         | _far_ outweighed by how much easier Python is to use than bash.
         | So I 'm giving up a 3/10 convenience but gaining a 10/10
         | convenience, which I think is worth it.
        
         | qznc wrote:
         | My principles is that I do not. If PyPI packages are needed,
         | rewrite it in Rust (or Go or D or whatever allows me to use
         | statically-linked libraries).
         | 
         | Python packages are fine for servers but not for CLI tools.
        
       | N_Lens wrote:
       | Weren't we already?
        
       | sevensor wrote:
       | The Python stdlib does not get enough credit. People complain
       | about things like how its http client is dated and slow, but it's
       | 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!
        
         | BoppreH wrote:
         | Absolutely agree, but it's funny that you mentioned graphlib.
         | It has a single algorithm (topological sort)!
         | 
         | The sqlite, tkinter, and shelve modules are the ones I find
         | most impressive.
        
         | zelphirkalt wrote:
         | Currently developing a tkinter app, and loving, that I don't
         | have to install an additional GUI framework. This will be much
         | easier to package than with GTK or QT or Pyside or something. I
         | am sure people have figured out all of that, but my project has
         | so minimal dependencies, and yet offers a full GUI.
        
           | capyba wrote:
           | tkinter is the best kept secret in the Python std lib.
           | 
           | Pair it with numpy and matplotlib (two external dependencies
           | that personally I consider part of Python itself), and you've
           | got 80% of an interactive scientific simulation environment.
        
             | zelphirkalt wrote:
             | In fact, I am planning to use matplotlib soon, to visualize
             | progress in learning, since my app is a language learning
             | app.
        
         | bigstrat2003 wrote:
         | I strongly agree! Sure, the stuff in the Python stdlib might
         | not be the best available - but it's _always there_. I 've been
         | in environments where I couldn't install requests but urllib2
         | meant that I could still get the job done. I think that every
         | language should have a batteries-included stdlib.
        
       | rjmill wrote:
       | Odd, I don't see any mention of subprocess.run, the workhorse of
       | python scripting.
       | 
       | Quick rundown for the unfamiliar:
       | 
       | Give it a command as a list of strings (e.g.,
       | subprocess.run(["echo", "foo"]).)
       | 
       | It takes a bunch of flags, but the most useful (but not
       | immediately obvious) ones are:                 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
       | 
       | 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.
        
         | bccdee wrote:
         | Also `asyncio.subprocess`, which lets you manage multiple
         | concurrently running commands. Very handy if you need to
         | orchestrate several commands together.
        
         | theomega wrote:
         | One thing I can recommend that makes scripting in python with
         | external commands a lot easier is the `sh` module:
         | 
         | https://pypi.org/project/sh/
         | 
         | Basically you can just `from sh import [command]` and then have
         | an installed binary command available as function
         | from sh import ifconfig       print(ifconfig("eth0"))
        
           | code_biologist wrote:
           | uv for using sh as a dependency in scripts, managed inline,
           | has changed it from "eh, I'll just use subprocess" to "why
           | not" for me.
           | 
           | https://docs.astral.sh/uv/guides/scripts/#using-different-
           | py...
        
             | grumps wrote:
             | I love uv, the why not works great if it's your machine but
             | in places without your machine uv is just another step for
             | a customer.
        
           | pxc wrote:
           | I've used Plumbum for this for some projects at work, and
           | really like it for this.
           | 
           | https://plumbum.readthedocs.io/en/latest/local_commands.html.
           | ..
           | 
           | It also does argument parsing and validation, so it's
           | generally pretty useful for writing little CLI tools that
           | invoke other CLI tools.
           | 
           | https://plumbum.readthedocs.io/en/latest/cli.html
        
           | grumps wrote:
           | `sh` is nice but it requires a dependency. No dependencies is
           | nicer IMHO. uv makes this way easier but for low dependency
           | systems, or unknown environments stdlib is king.
        
           | btown wrote:
           | I love how this import trick shows how hackable Python is -
           | and it's this very hackability that has led to so many of the
           | advances we see in AI. Arguably without operator overloads
           | we'd be 5 or more years behind.
           | 
           | https://github.com/amoffat/sh/blob/2a90b1f87a877e5e09da32fd4.
           | ..
        
           | theamk wrote:
           | Please don't use "sh" python library!
           | 
           | By default (1) captures stdout and stderr of all processes
           | and (2) create tty for processs stdout.
           | 
           | Those are really bad defaults. The tty on stdout means many
           | programs run in "interactive" rather then "batch" mode:
           | programs which use pager get output truncated, auto-colors
           | may get enabled and emit ESC controls into output streams (or
           | not, depending on user's distro... fun!). And captured stderr
           | means warnings and progress messages just disappear.
           | 
           | For example, this hangs forever without any output, at least
           | if executed from interactive terminal:                   from
           | sh import man         print(man("tty"))
           | 
           | Compare to "subprocess" which does the right thing and
           | returns manpage as a string:                   import
           | subprocess         subprocess.check_output(["man", "tty"],
           | text=True)
           | 
           | Can you fix "sh"? sure, you need to bake in option to disable
           | tty. But you've got to do it in _every_ script, or you'll see
           | failure sooner or later. So it's much easier, not to mention
           | safer, to simply use "subprocess". And as a bonus, one less
           | dependency!
           | 
           | (Fun fact: back when "sh" first appeared, everyone was using
           | "git log" as an example of why tty was bad (it was silently
           | truncating data). They fixed it.. by disabling tty only for
           | "git" command. So my example uses "man" :) )
        
             | petters wrote:
             | > They fixed it.. by disabling tty only for "git" command.
             | 
             | Wow... yes sounds like a library to avoid!
        
           | Lvl999Noob wrote:
           | And for the opposite, where you keep your main pipeline in
           | shell but want to use python for some parts of it, there is
           | pypyp.
           | 
           | https://pypi.org/project/pypyp/
           | 
           | It takes cares of the input and output boilerplate so you can
           | focus on the actual code that you wanted python for.
           | > seq 1 5 | pyp 'sum(map(int, lines))'         > ls | pyp
           | 'Path(x).suffix'
        
             | two_handfuls wrote:
             | So cool! I made pawk [1] to get some of the same features,
             | but yours is better! Congrats!
             | 
             | [1] https://github.com/jean-philippe-martin/pawk
        
           | zelphirkalt wrote:
           | Does this give live output (meaning before completion) of
           | processes run?
        
         | albertzeyer wrote:
         | I think the point is that for most things, you don't need to
         | call any external tools. Python's standard library comes
         | already with lots of features, and there are many packages you
         | can install.
        
       | tayo42 wrote:
       | Pretty much anything longer then a throwaway one liner I write in
       | python.
       | 
       | Would be cool if python had a pipe operator though.
       | 
       | 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.
        
         | mackeye wrote:
         | nushell (https://www.nushell.sh/) is essentially perfect for
         | this use case! i use it rather than posix(y) shells for most
         | projects where id normally reach for python.
        
         | kstrauser wrote:
         | You can always set shell=True and pass in an entire command
         | line as a string, but... don't do that. It seems really nice
         | until the first time you get the shell escaping wrong, and then
         | it's something you tend never to do again.
         | 
         | For example,                 subprocess.run("rm -rf ~/ some
         | file", shell=True)
         | 
         | and                 subprocess.run(["rm", "-rf",  "~/ some
         | file"])
         | 
         | have significant different behavior.
        
           | lgas wrote:
           | What is the difference in behavior? They both look like they
           | would delete the user's home directory. I assume the latter
           | would try to delete a directory literally named with a tilde
           | instead?
        
             | kstrauser wrote:
             | The latter passes each item in the list into the child
             | processes's argv, as-is, without the shell parsing them.
             | That means this would delete a single item named "~/ some
             | file", spaces and all, instead of three items named "~/",
             | "some", and "file".
             | 
             | Edit: I'm typing this on my phone, so brevity won over
             | explicitness. The latter probably wouldn't expand ~.
             | Imagine a file named "/home/me/ some file" for a better
             | example.
        
         | vovavili wrote:
         | cytoolz.pipe (https://toolz.readthedocs.io/en/latest/api.html#t
         | oolz.functo...), cytoolz's curried namespace
         | (https://toolz.readthedocs.io/en/latest/curry.html#the-
         | currie...) and method chaining can pretty much fully replace a
         | pipe operator. The only problem is that you don't really want
         | to write anonymous functions in Python with its needlessly
         | verbose syntax, and long pipes are kind of limited without
         | that.
        
       | davidkhess wrote:
       | Xonsh is perfect for this: https://xon.sh
        
       | abhiyerra wrote:
       | I have been converting a lot of my makefiles to pyinvoke and
       | fabric and it makes things so much easier to manage than bash or
       | make. Don't know why I held on for so long.
        
       | Rendello wrote:
       | I've never liked shell scripting. Last year, I switched my build
       | system of a Rust project over to Python (Cargo is actually quite
       | limited as a build system). For a newer project, I'm using Rust
       | itself with the XTask pattern. I'm not sure if I prefer the
       | Python or Rust approach yet.
        
         | seabrookmx wrote:
         | I quite liked SCons back when I wrote C++!
        
       | kkfx wrote:
       | Python for scripting honestly is Xonsh :)
        
       | gabrielsroka wrote:
       | > Python is installed on pretty much every machine
       | 
       | > Python 3 is installed on basically every machine out there.
       | 
       | > Python will work the same on all the machines you run your
       | script on
       | 
       | No, no, and no.
        
         | nilslindemann wrote:
         | More like, "no, but installation is easy", "yes, if the machine
         | is safe", "depends on what you do"
        
         | qznc wrote:
         | At work my machine has probably ten or more installations of
         | Python hidden in various tools. I'm certainly not alone. So we
         | could say "on average Python is installed on every machine". /s
        
       | GutenYe wrote:
       | Same, use Javascript for Scripting.
       | https://github.com/gutenye/script.js
        
       | archargelod wrote:
       | If a script is simple - I use posix sh + awk, sed, etc.
       | 
       | But if a script I write needs to use arrays, sets, hashtable or
       | processes many files - I use Nim[0]. It's a compiled systems-
       | programming language that feels like a scripting language:
       | 
       | - Nim is easy to write and reads almost like a pseudocode.
       | 
       | - Nim is very portable language, runs almost anywhere C can run
       | (both compiler and programs).
       | 
       | - `nim r script.nim` to compile and run (cached on subsequent
       | runs) or use a shebang `#!/bin/env -S nim r`
       | 
       | - Nim programs are fast to compile (use debug mode and tcc
       | compiler for almost instant compile times)
       | 
       | - Nim scripts run very fast <10ms (something that was very
       | annoying to me with bash and Python)
       | 
       | - good chances you don't need external dependencies, because
       | stdlib is batteries included and full of goodies.
       | 
       | - if you need external deps - just statically link them and
       | distribute a cross-compiled binary (use zigcc[1] for easy Nim
       | cross-compilation).
       | 
       | [0] - https://nim-lang.org
       | 
       | [1] - https://github.com/enthus1ast/zigcc
        
         | pxc wrote:
         | > If a script is simple - I use posix sh + awk, sed, etc.
         | 
         | > But if a script I write needs to use arrays, sets, hashtable
         | or processes many files
         | 
         | One option that I sometimes use at work (in addition to writing
         | some Python CLIs) that is a pretty nice next step on this
         | spectrum is Bash-with-all-the-fixins'.
         | 
         | I use a Nix-based dependency resolver for shell scripts called
         | resholve1 to parse scripts so that it can identify all of their
         | dependencies (including Bash itself), then produce a "compiled"
         | script as a Nix build where all of the references to external
         | programs are replaced with pinned Nix store paths.
         | 
         | Then I have a fixed (and recent) version of GNU Bash regardless
         | of platform, so I'm free to use Bash features that are newer or
         | nicer than POSIX sh. My favorite such features are `mapfile`
         | and `lastpipe` for writing in a more functional style, plus of
         | course maps ("associative arrays").
         | 
         | I don't have to worry about portability problems with common
         | utilities, because my scripts will bring along the expected
         | implementations of coreutils, `find`, `grep`, etc. I'm free to
         | use "modern" alternatives to classic utilities (like `rg`
         | instead of GNU grep or `fd` instead of GNU findutils) if they
         | offer better performance or more readable syntax. Polished
         | interactivity is easy to build by just embedding a copy of
         | `fzf`. And while I don't love Bash for working with structured
         | data, it becomes a lot less painful when I can just pull in
         | `jq`.
         | 
         | It's obviously got some disadvantages versus an option like
         | Python, but the amount-of-code to functionality ratio is also
         | much more favorable than Python's.
         | 
         | I typically use this for scripting build and development tasks
         | in projects that already use Nix (via Devenv-- I have some
         | unpublished changes to the scripts module that add resholve and
         | ShellCheck integration) to manage their environments, so
         | there's no special packaging/distribution/setup burden.
         | 
         | IME it makes for vastly more readable and maintainable shell
         | scripts than painstakingly limiting oneself to pure POSIX sh,
         | so it can serve quite well as a middle ground option between
         | barebones sh and a "real" programming language as your little
         | script gradually grows in complexity.
         | 
         | > if you need external deps - just statically link them and
         | distribute a cross-compiled binary (use zigcc[1] or easy Nim
         | cross-compilation).
         | 
         | The deployment story sounds very slick and hard to beat! This
         | is something I might want to try for scripts I want to be easy
         | to distribute on macOS systems that don't have Nix installed.
         | 
         | --
         | 
         | 1: https://github.com/abathur/resholve
        
         | slaymaker1907 wrote:
         | I might need to try it out. However, I haven't really found a
         | use case yet where the speed of Python has been a major factor
         | in my day job. It's usually fast enough and is a lot easier to
         | optimize than many languages.
         | 
         | I actually sped up a script the other day that had been written
         | in bash by 200x by moving it over to Python and rewriting the
         | regexes so they could run on whole files all at once instead of
         | line by line. Performance problems are most often from poorly
         | written code in my experience, not a slow language.
        
       | kazinator wrote:
       | > By the way, don't add a comma after the elements in the list,
       | ...
       | 
       | Python:                 forty_two = ( 42 )
       | tuple_containing_forty_two = ( 42, )
        
         | sevensor wrote:
         | You don't even need parentheses here.                  42,
         | 
         | This expression is a tuple all by itself.
        
       | kazinator wrote:
       | cp version.template build/version.txt            sed -i
       | "s/@VERSION@/${COMMIT_TAG:-dev}/" build/version.txt       sed -i
       | "s/@BUILD_DATE@/${BUILD_DATE}/" build/version.txt
       | 
       | Eww! Mutating a file in place, and needing a GNU extension for
       | it.                  sed \          -e
       | "s/@VERSION@/${COMMIT_TAG:-dev}/" \          -e
       | "s/@BUILD_DATE@/${BUILD_DATE}/" \          <
       | build/version.template > build/version.txt
        
       | headcrash wrote:
       | why not use perl?
        
       | headcrash wrote:
       | Why not use Perl?
        
         | wiseowise wrote:
         | Because it is write-only.
        
         | DonHopkins wrote:
         | Because of its culture, and what it does to your brain.
        
         | zbentley wrote:
         | Because the instant I want a dependency, I'm screwed. Half of
         | what I want isn't on CPAN, and the other half is ten years out
         | of date and requires a compiler toolchain, fifty undocumented
         | build dependencies at nonstandard paths, and five
         | obscure/package-specific environment variables set in order to
         | install. Python? Python either has it in the stdlib or has
         | wheels available for pretty much everything.
         | 
         | Talking super simple stuff here, too: database drivers,
         | markdown formatters, structured data parsers.
        
       | binary132 wrote:
       | I like using Go for "scripting". To each their own I suppose.
        
         | Alifatisk wrote:
         | Is this comment some kind of subliminal message?
        
       | yoavsha1 wrote:
       | I strongly agree with this. At $WORK I usually work on projects
       | comprising many small bits in various languages - some PowerShell
       | here, some JS there, along with a "build process" that helps
       | minify each and combine them into the final product. After
       | switching from shell scripts to Just and having to deal with a
       | ton of issues on the way (how does quoting work in each system?
       | How does argument passing? Environment variables?) I simply wrote
       | a simple script with Python, UV shebang and PEP723 dependencies.
       | Typer takes care of the command line parsing, and each "build
       | target" is a simple, composable, readable python function that
       | takes arguments and can call other ones if it needs to. Can't be
       | simpler than that and the LLMs love it too.
        
       | Alifatisk wrote:
       | I like the message the article is trying to convey, Python is
       | good alternative to complicated shell scripts in my opinion.
       | 
       | I do wonder, let's say the scripting file is using lots of
       | libraries, do you have to include some kind of requirements.txt
       | file with it aswell when you want to share it with other people?
       | 
       | In Ruby, there is inline bundler which makes sharing a single
       | Ruby script very portable.
       | 
       | https://bundler.io/guides/bundler_in_a_single_file_ruby_scri...
        
         | zelphirkalt wrote:
         | One would be well-advised to provide not only a
         | requirements.txt file, but also a lock file, if the use case is
         | important enough.
        
         | bccdee wrote:
         | uv does that these days:                 # /// script       #
         | dependencies = [       #   "requests<3",       #   "rich",
         | # ]       # ///              import requests
        
       | pxc wrote:
       | I wrote another comment here about a strategy for writing
       | portable Bash scripts without compromising on features and freely
       | using arbitrary external commands. I wanted to give an example
       | from the article of how I'd likely write one of his examples of a
       | "somewhat unreadable shell script" in this style.
       | 
       | His ugly sh example:                 morning_greetings=('hi'
       | 'hello' 'good morning')       energetic_morning_greetings=()
       | for s in "${morning_greetings[@]}"; do
       | energetic_morning_greetings+=( "${s^^}!" )       done
       | 
       | and his more readable Python equivalent:
       | morning_greetings = ['hi', 'hello', 'good morning']
       | energetic_morning_greetings = \          [s.upper() + '!' for s
       | in morning_greetings]
       | 
       | And I'd write the shell version in Bash something like this:
       | morning_greetings=(hi hello 'good morning')       printf '%s!\n'
       | "${morning_greetings[@]}" \         | tr '[:lower:]' '[:upper:]'
       | \         | readarray -t energetic_morning_greetings
       | 
       | Does it still involve more syntax? Yeah. Printing arrays in Bash
       | always involves some. But it's clearer at a glance what it does,
       | and it doesn't involve mutating variables.
       | 
       | The piece this example is too simple to show (since it's focused
       | only on data operations and not interacting with the filesystem
       | or running external programs) is how much shorter the Bash
       | usually ends up being than the Python equivalent.
        
       | ggm wrote:
       | This from the site:                 /*       Oh, you're looking
       | at my CSS. Here be dragons.            Let's bundle in a rant
       | while you're here: I'd like to use variable width fonts
       | instead of fixed size fonts. I enjoy 300 (i.e. light) and 600
       | (i.e. semibold)       over regular and bold for Crimson Pro only.
       | But Chrome doesn't like to show       anything but 400 and 700
       | with variable width fonts, so I'll have to trick Chrome       by
       | declaring my 300 and 600 fonts as if they were 400 and 700
       | respectively.            ... or well, I could use font-variation-
       | settings, but that's a global override       which means I have
       | to specify it EVERYWHERE and that's just too much effort for
       | poor me.       */
        
       | bjoli wrote:
       | Funny this shows up on HN now! Jean Niklas wrote his thesis about
       | RRB trees, which is an evolution of the persistent vectors of
       | clojure fame. I have spent many hours reading his thesis lately,
       | because I just spent two months porting his c-rrb
       | (https://github.com/hypirion/c-rrb) to c#, which was a fun
       | endeavour. I wish I would have read hus thesis better since I
       | spent two weeks debugging issues that arise from me not enforcing
       | the leftwise dense invariant with regards to the tail.
        
       | idoubtit wrote:
       | So they suggest to write scripts in Python rather than shell
       | because Python is stable, probably installed on the target
       | machine, has a big standard library, and is more readable. Many
       | people do so.
       | 
       | That's the bright side of Python. They should mention the dark
       | side, or Why _not_ to use Python for scripting.
       | 
       | First of all, the promise of easy portability breaks as soon as
       | the script has dependencies. Try to install some Python program
       | on a server where you're not root and a minimal python3 is
       | installed.
       | 
       | The stability isn't very good in my experience either. I've often
       | seen programs not compatible with recent releases of Python,
       | either explicitly in the README or implicitly at runtime.
       | Unmaintained Python code breaks.
       | 
       | Unfortunately, there is no silver bullet. Posix shell or bash may
       | be better for simple scripts; Perl or Python if you know you
       | won't require dependencies or if you have a good control on where
       | to install the script; languages that compile to static
       | executables are not really "scripting", but may be a better
       | choice for long(term usage. These past years, I tend to keep away
       | from Python as much as I can.
        
         | skydhash wrote:
         | Python relies on env variables to find where to load modules.
         | Updating those variables is what venv does essentially.
        
         | wodenokoto wrote:
         | > First of all, the promise of easy portability breaks as soon
         | as the script has dependencies.
         | 
         | And bash has a good dependency story? At least with python you
         | can bundle your script with a requirements.txt file and it is
         | doable for the target machine to get up and running.
        
       ___________________________________________________________________
       (page generated 2025-12-14 20:01 UTC)