Skip to content

Why I Love Python, But I Still Write Tools in BASH

David Bank edited this page Mar 26, 2024 · 5 revisions

Python - The Interpreted Language I Always Wanted

For interpreted scripting languages, I love Python... or at least really like it.

The number one reason? It forces good code hygiene, which helps keep code understandable.

Seriously, look at Perl. There was an actual Obfuscated Perl contest. Perl programmers actually prided themselves on writing difficult-to-comprehend Perl. And making incomprehensible Perl wasn't very hard.

Yes, there are ways to obfuscate Python, but it's generally done for code-security reasons (see PyArmor) - although bad actors will use similar techniques. But if there was a coding ethos associated with Python, the idea of creating, deliberately and with pride, difficult-to-comprehend Python code always seemed counter to it.

I note with some dismay the creation, starting in 2023, of an "Obfuscated Python" contest (to which I refuse to link). Prior to that, similar attempts were generally limited to esoteric versions of "Hello World".

To be sure, Perl is hardly unique. I recall "Obfuscated C" contests before Python or even Perl were created. Perhaps I'm prejudiced, but I find the contest idea anathema for interpreted, as opposed to compiled, languages.

So Why Is Most Of My Repo in BASH?

Given how much I like and respect Python, that's a fair question.

The major reason is that most of my tools are designed to automate OS tasks, and/or to extract information about the OS or hardware. As much as it pains me to say this, I often found trying to do those things in Python a frustrating experience.

First, the environment where I started developing some of my tools originally consisted of RHEL v2.1 through v4, so all I had was Python v2. Moreover, the modules varied wildly. Finding the right modules that worked the way I needed in all RHEL versions we had was impossible.

Even once newer RHEL appeared and started to provide Python v3, due to intransigence from our upper management, we still had an environment with (at peak) N-4 RHEL versions. So I couldn't write in Python v3 and have it run on older systems, and the divergence of modules, and versions of modules, across the environment as a whole made it impossible to maintain a coherent Python codebase for a given tool. Basically, I would have ended up with a bespoke set of tools for every RHEL version.

Yes, eventually, after system breaches and hardware failures made it impossible for upper management to ignore the exorbitant costs of running N-4 RHEL versions, we finally (about the time RHEL v7 appeared) got rid of everything prior to RHEL v5. However, by then, I'd already implemented the vast majority of my tools using BASH, and I didn't have the cycles to go back and re-implement in Python. And that was still N-3 versions of RHEL, but at least they all supported Python v3.

Which brings me back to the module problem.

For example, some of my tools needed to access DMI information - there's py-dmidecode, right? Sure is. And I found that the information it returned, and the format in which it returned the information, could vary wildly across versions of the module. The version that shipped with RHEL v5 might not report some of the DMI Types that the version shipped with RHEL v6 would report. Or even vice-versa. Or one version returned a particular bit of data in a string and another did it in a dictionary. And so on. Again, nearly impossible to maintain a unified codebase.

So I kept using BASH, because it worked fairly consistently across RHEL versions. Yes, I occasionally had to deal with output variations from OS tools, but those were easier to handle.

Using Python Just To Call Executables Is A Waste

The other issue was that so many of my tools worked by extracting information that was, generally, readily-available via OS-provided executable tools (for example, ethtool, ip, route, sysctl, etc). In contrast, Python often didn't have modules with a substantially-equivalent functionality.

As a result, when I looked at re-implementing my tools in Python v3, I found I would spend a lot of time using os.exec() and friends, and parsing the output. Which is what I was already doing in BASH, without having to worry about the effects of Python version variations across our insanely-backleveled environment.

Personally, my opinion is that if all Python is going to do is invoke some executables and parse, then that's a waste of Python. Never mind the overhead of loading the interpreter; it turns Python into... well... BASH. I don't see the advantage to doing that - it just increases tech debt.

Bottom Line

I really like well-written Python. I'm glad Python v3 has settled some long-standing Pythonic issues (although the curmudgeon in me still would like to see support for case). And I look forward to the day when there's a supported suite of modules that let me finally re-implement my tools in native Python without having to constantly os.exec().