logoalt Hacker News

Using Python for Scripting

93 pointsby birdculturelast Monday at 8:52 AM71 commentsview on HN

Comments

rjmilllast Friday at 3:36 PM

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.
show 3 replies
sevensorlast Friday at 3:31 PM

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!

show 2 replies
archargelodyesterday at 3:50 AM

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

show 1 reply
gabrielsrokalast Friday at 9:13 PM

> 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.

show 2 replies
mubou2last Monday at 9:46 AM

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-...

show 10 replies
kazinatoryesterday at 4:02 AM

  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
Alifatiskyesterday at 9:56 PM

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...

davidkhesslast Friday at 3:52 PM

Xonsh is perfect for this: https://xon.sh

yoavsha1yesterday at 8:59 PM

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.

pxcyesterday at 11:13 PM

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.

Rendellolast Friday at 6:06 PM

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.

show 1 reply
GutenYeyesterday at 12:27 AM

Same, use Javascript for Scripting. https://github.com/gutenye/script.js

abhiyerralast Friday at 5:21 PM

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.

kazinatoryesterday at 3:56 AM

> By the way, don’t add a comma after the elements in the list, ...

Python:

  forty_two = ( 42 )

  tuple_containing_forty_two = ( 42, )
show 1 reply
binary132yesterday at 8:44 PM

I like using Go for “scripting”. To each their own I suppose.

show 1 reply
N_Lenslast Friday at 3:13 PM

Weren't we already?

headcrashyesterday at 8:23 PM

Why not use Perl?

kkfxlast Friday at 8:29 PM

Python for scripting honestly is Xonsh :)

headcrashyesterday at 8:23 PM

why not use perl?

tayo42last Friday at 3:50 PM

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.

show 2 replies