News for hackers. Stuff that matters.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by yjftsjthsd-h on Friday March 25, 2022 @01:10PM (#30804705)
I was hoping someone finally had a library or something to make python good at the things shell does well (mostly, running stuff and piping output around). If only.

This really just reads like someone who doesn't know shell and does know python, but thinks the problem is the tool. They continually describe shell with phrases like "obscure and difficult-to-predict" while talking through things that are either obvious with experience or from context, and then write something different in python (i.e. something you could write in BASH that does something different) that's not less obscure but is only using python obscurity.

> An example of a shell obscurity is the way the current working directory is set. The cd command is clear enough, but in the presence of sub-shells using (), can make it difficult to discern a stack of nested shell invocations and how the working directory changes when the sub-shells exit.

Er... yeah? So you expect PWD to be a global and it was actually a local?

Or more explicitly:

> One of the shell’s ickier features is that variables tend to be global. There are some exceptions and caveats, however, that lead to shell scripts that are broken or behave inconsistently.

That's not a shell feature, that's just how people frequently write it - you can make functions and declare your variables local if you want. I can easily write a python script with all my variables at the top, too.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by deathanatos on Saturday March 26, 2022 @01:01AM (#30809364)
We had some problem, roughly, "run a tool on all YAML files in the directory" or something. The first attempt that led to a production outage:

(It is late, bash examples are approximate. Don't sweat the syntax.)

  the_tool *.yaml
There weren't any YAML files, so argv[1] was literally "*.yaml", which then wasn't found, leading to errors, etc.

  shopt -s nullglob
  the_tool *.yaml
(Don't even get me started on how there's shopt -s and set -o and WTF.)

There weren't any YAML files, so argc was == 0, which the program considered an error. Mea culpea, perhaps.

  AN_ARRAY=(*.yaml)
  if [[ < length of the array is not 0 > ]]; then
    the_tool "${AN_ARRAY[@]}"
  fi
This failed, b/c bash can't represent an empty array! (And we run -u, as one must, if one wants reasonable behavior.)

So finally the contorted,

  AN_ARRAY=(*.yaml)
  if [[ "${AN_ARRAY+x}" = x && < length is not 0 > ]]; then
    the_tool "${AN_ARRAY[@]}"
  fi
To say "you're holding it wrong" is just folly. Bash is worthy of the same treatment that "A Fractal of Bad Design" gave PHP, and it has no place in anything that wants to call itself engineering.

Use it on the command line in your day to day, fine; but if it's getting automated, jump to at least Python.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by Arnavion on Saturday March 26, 2022 @01:57AM (#30809606)

    find . -maxdepth 1 -name '*.yaml' -print0 | xargs -r0 the_tool
>To say "you're holding it wrong" is just folly. Bash is worthy of the same treatment that "A Fractal of Bad Design" gave PHP, and it has no place in anything that wants to call itself engineering.

If you have a problem that involves "run a command with a list of parameters but not if the list is empty" and you don't immediately think of `xargs -r`, then consider that it's because you don't understand the tools, not because the tools are bad.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by rascul on Saturday March 26, 2022 @09:53AM (#30811775)
> If you have a problem that involves "run a command with a list of parameters but not if the list is empty" and you don't immediately think of `xargs -r`, then consider that it's because you don't understand the tools, not because the tools are bad.

find has -exec, xargs isn't necessary.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by Arnavion on Saturday March 26, 2022 @02:57PM (#30814326)
`-exec` would run the command for each file separately, which isn't what the original problem wanted, and I didn't want to assume that it would be equivalent.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by Izkata on Saturday March 26, 2022 @03:30PM (#30814626)
It runs them separately if you end the exec with ";", it appends as many as possible to the same command if you end it with "+"
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by Arnavion on Saturday March 26, 2022 @04:18PM (#30815008)
Ah yes, I always forget about that.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by deathanatos on Monday March 28, 2022 @08:21PM (#30837198)
I'll submit that that's a solution.

But it's like, I have to turn my head around and stop doing one thing (globs), and switch over to another thing (find, xargs). The language has baited me down one way of doing it, when I shouldn't be doing it that way. I'm well aware of xargs (but not the -r flag). I typically avoid xargs, as our code has to¹ work on macOS & Linux, and xargs is one of those utilities whose available options differ between the two for even simple use cases. (Such as here, for the "does it exec on no args?".)

But it still proves the point: the equivalent Python is just so much more straight-forward, easier to read, and not apt to shoot me at a moments notice, and doesn't have bugs. A reader only familiar with Python's syntax can read and understand Python, and can write Python that is correct. Not so with bash, you have to understand, "oh, no, globs will screw you here, you need this flag from this utility" — no, it's lunacy.

Like, I can go down the road you're suggesting (and in the past, I did) and keep learning the nooks and crannies and intricacies of bash, each one adding to the nightmare fuel that is my understanding of shell. I can keep tweaking the script around various failure modes, letting each line fail in prod 3 different ways. Or, I can just re-write it in Python, or Rust, or any other sane language and have it never fail again, for reasons that would also either apply to bash, or apply and have worse consequences than bash. (E.g., the Python would raise, bash would just plow on with it.)

Your response is exactly the same as my response before I saw the light on PHP, C, etc.: "you're holding it wrong". No, it's a bad tool that guiles the user into holding it wrong. There are better tools that are easier to hold.

¹b/c despite all work being done for Linux, devs get MBPs, b/c the company can't manage more than exactly 1 type of machine, for everyone.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by seanp2k2 on Saturday March 26, 2022 @04:04PM (#30814890)
Bash is amazing at being expressive and allowing for a ton of common things sysadmins / Devops / power users need to do with just one or a few lines of code vs a “proper” language that might require libraries, compiling stuff, or just a ton more SLOC + time debugging. If you don’t know what a glob is going to match, try it first. Understand the semantics. If you need portability, do that too, but a ton of what I end up using bash for is along the lines of slicing up poorly formatted inconsistent lists and running tools against stuff, possibly in conjunction with args from those lists, and for that it’s amazing. Stuff like jq helps a lot, and tools like xmlstarlet before that were great too. I remember trying to rewrite a script that was getting long and AWKward in Python and getting pretty frustrated with how explicit and error-prone it was there. Sure, the result was probably more robust, but it came at the expense of an order of magnitude more dev / debugging time. That’s not always necessary; sometimes “good enough” is good enough, and usually bash is good enough.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by raffraffraff on Saturday March 26, 2022 @06:32AM (#30810691)
Also, how much code would it take to do the same thing in python?
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by kleene_op on Saturday March 26, 2022 @06:49AM (#30810749)
Probably something along the line of

  Popen(["the_tool"] + list(Path().glob("*.yml")))
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by mannykannot on Saturday March 26, 2022 @08:57AM (#30811425)
This is reasonable in general, though in this case, I think this will replicate the problem of the second Bash version, as the problem there is with the_tool requiring at least one argument.

While dethanatos's point about Bash's default behavior with a non-matching glob is on target, the remainder of the example demonstrates a problem in the_tool, which will need to be worked around regardless of how you invoke it.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by kaba0 on Saturday March 26, 2022 @01:36PM (#30813699)
Doesn’t xargs also die at too many files? Fact is, bash manages to have more footguns than goddamn C, which is telling something
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by Arnavion on Saturday March 26, 2022 @03:00PM (#30814342)
>Doesn’t xargs also die at too many files?

No?

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by DyslexicAtheist on Saturday March 26, 2022 @04:32AM (#30810226)
> To say "you're holding it wrong" is just folly. Bash is worthy of the same treatment that "A Fractal of Bad Design" gave PHP, and it has no place in anything that wants to call itself engineering.

is this a cloud AMD64 opinion that ignores the 2 trillion devices which Gartner predicts will never run python?

I recently finished a ~600 lines ash script and the kids at work which just graduated complained that *"Y u no python?" For them it was difficult to understand because apparently I avoided cat/grep/dirname/basename/etc and used shell syntax wherever possible to avoid shelling out. (the thing I did would only run when system reaches a certain load)

Everyone forgot that the target hardware has only busybox (ash) and was massively resource constrained. My only options were to implement what I was doing as an eBPF, in C, or shell (ash). The shell script took 4 days (and was refactored/optimized several times within that period), the C (or eBPF) program would have taken ~10 days and the eBPF which is the best solution is unmaintainable once I leave the company.

from a performance perspective I take a bash script any time. Also python programs over time have a risk of eventually pulling in a dozen dependencies.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by rhn_mk1 on Saturday March 26, 2022 @04:38AM (#30810243)
Judging by the reaction of the kids at work, what you did is already unmaintainable.

Plus, if you are running shell out of necessity on a constrained system, that doesn't mean shell deserves to be a programming language where there's actual choice.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by DyslexicAtheist on Saturday March 26, 2022 @05:13AM (#30810387)
> what you did is already unmaintainable.

how so? there is no python or perl or ruby etc on the target. what would have been a maintainable option other than /bin/sh ? C/C++ ?

> where there's actual choice.

again what choice? :) as I said there is no other solution than what I've given. the system literally doesn't have anything else regardless what "I think" it should support.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by psd1 on Saturday March 26, 2022 @06:15AM (#30810625)
Anything is unmaintainable if the maintainers don't have the foundational skills.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by teddyh on Saturday March 26, 2022 @07:38AM (#30810986)
In which case those people are not actually maintainers.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by tempodox on Saturday March 26, 2022 @07:23AM (#30810906)
If the kids refuse to learn anything new (to them), they chose the wrong occupation.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by kaba0 on Saturday March 26, 2022 @01:37PM (#30813706)
How is bash performant? It is literally interpret by lines, and starting a whole process per if statements..
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by bdowling on Sunday March 27, 2022 @02:53AM (#30818304)
> How is bash performant?

Typically, time spent interpreting control flow in a bash script is dwarfed by time spent in the programs called by the script.

> It is literally interpret by lines

It's just splitting lines into words to build up commands. It's not compiling C++.

> starting a whole process per if statements..

In the common pattern `if command1; then command2; fi`, if command1 is a builtin (e.g., `test` or `[`), then bash does not create a subprocess.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by kaba0 on Sunday March 27, 2022 @04:55AM (#30818786)
Still doesn’t make it fast in any meaning of the word, which was my original criticism.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by bdowling on Sunday March 27, 2022 @01:37PM (#30822185)
You asked how bash is performant. I responded with reasons its performance is good in many applications. Here”s one more: bash starts up faster than other interpreters (e.g., Python), and startup time can often be a significant factor in overall performance.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by Izkata on Saturday March 26, 2022 @03:34PM (#30814660)
Guessing: Automatic parallelization in pipelines, just as long as there isn't a wall that has to consume all its input like "sort".
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by yjftsjthsd-h on Saturday March 26, 2022 @01:46AM (#30809547)
On the contrary, I am 100% open to suggestions that BASH sucks. (Personally, I would have picked the inadequacies of `set -e` as my favorite ... unfortunate aspect.) My objection was very narrow: this specific article does a poor job at saying why shell sucks, and a worse job still at presenting Python as a superior alternative. That's not to say that good reasons to use something else don't exist, and it's not even to say that Python can't be that alternative. (Your sibling comments suggesting xonsh and https://amoffat.github.io/sh/ paint a fairly compelling way to actually move to python without losing the advantages of shell, for instance.)
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by 1vuio0pswjnm7 on Saturday March 26, 2022 @03:49AM (#30810063)
Personally, I never use bash for scripting. I use the system's scripting shell, Debian's Dash or NetBSD Ash. Rhetorical question: If Bash was great for shell scripting, why doesn't the OS project choose it for scripting. When I compile the OS kernel and userland, the project (NetBSD) uses shell scripts not Python. Not suggesting the shell is necessarily better but I am suggesting it works and people understand it.

There are many ways to run a command on every .yaml file in a directory. Personally I like using xtrace. Something like:

   echo cd the_directory > 1.sh
   echo "test -f *.yaml" >> 1.sh
   ls -1 *.yaml|sed 's/^/the_tool /' >> 1.sh
   chmod +x 1.sh
   less 1.sh
   sh -ex 1.sh
As mostly a day-to-day shell user who automates some things, I do not use Python. Never had an outage.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by Izkata on Saturday March 26, 2022 @02:02AM (#30809627)
My preferred way under those conditions would be more along the lines of "see if a safe command fails, do the desired command if not", like:

  if ls *.yaml > /dev/null; then
    the_tool *.yaml
  fi
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by xorcist on Saturday March 26, 2022 @07:23AM (#30810902)
That only looks simple because we don't know what problem was being solved.

Is it really right to not process any files just because one of them just disappeared?

In general, situations like this suffer from race conditions and time-of-check/time-of-use problems.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by Izkata on Saturday March 26, 2022 @03:41PM (#30814731)
> Is it really right to not process any files just because one of them just disappeared?

No idea what you mean here, the glob can expand to one or many.

> In general, situations like this suffer from race conditions and time-of-check/time-of-use problems.

I mean, that's a problem in every version presented so far, one that I don't think can be fixed externally without something extra like hardlinking to a new directory, because "the_tool" apparently can't handle missing files and they can go missing when "the_tool" starts up. That's why I didn't even bother.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by xorcist on Saturday March 26, 2022 @07:16AM (#30810874)
This tool of yours can not cope with files not existing, and causes production outages? Yet somehow you manage to blame bash for this? The outcome would not have been different if systemd or python started your tool.

In fact, handling that types of errors is what bash is suitable for. "If file is readable, process it" is not an unusual construct. Please do take care however to avoid races if this directory is a spool directory that can be populated at any time, in which case it is useful that rename() is atomic, sometimes an additional working directory is required. Again, this is how the operating system is designed and nothing the shell can gloss over.

I have seen many problems from previously unknown race conditions when concurrency is increased. Scripts have to be kept composable and trivial to understand in order to guard against that.

To be honest, I would argue to remove any script that looks like the last example above. It is not clear what side effects of that bizarre array construct are useful to the system. Keep scripts as simple and readable as possible, a trivial truth perhaps, applicable to every language, but one that needs to be reiterated until the end of our profession.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by sfoley on Saturday March 26, 2022 @09:20AM (#30811565)
> This failed, b/c bash can't represent an empty array!

It absolutely can, I have no idea where you’re getting this idea.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by theteapot on Saturday March 26, 2022 @02:42AM (#30809800)
What happens if someone runs `the_tool this-file-does-not-exist.yaml` or the yaml file is removed by some other means before you get a chance to process it? Does your world also come tumbling down in these cases? If yes, please stop blaming Bash for it.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by theteapot on Saturday March 26, 2022 @02:32AM (#30809757)
Didn't know "Fractal of Bad Design" even existed. I like aspects of PHP. Sorry.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by zaphirplane on Saturday March 26, 2022 @02:57AM (#30809855)
This isn’t you are holding it wrong It is in the “do as I want not as I say” category
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by blueblob on Friday March 25, 2022 @09:50PM (#30808615)
I agree with your sentiment. People like to complain about tools. The nice things about bash (and even sh) are that you can combine a bunch of simple tools together quickly because the syntax doesn't get in the way of it. The language designers didn't intend for people to be writing thousands of lines of scripts; they intended them to be using them locally and focused on interactivity. So naturally the syntax is nice for interactive work. It's a stretch to pretend that working with python paths is easier than working with shell paths, just like it's a stretch to say your bash machine learning library is better than a python one. The author should let things excel at what they're good at and stop pretending everything needs to be unified.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by yjftsjthsd-h on Friday March 25, 2022 @10:34PM (#30808798)
After going over it again, I think I can be more precise: What annoys me is that it's inconsistent to the point of being disingenuous. Like... You want to say error handling is hard to do right? That's fine, it is. You wanna write "Checking the status of programs using $? can be accidentally left off, leading to inconsistent behavior" and then in the same article write in explicit exception handlers, and act like somehow those can't be forgotten!? Get lost.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by psd1 on Saturday March 26, 2022 @06:13AM (#30810611)
If you forget an error handler in python, execution terminates. So I don't see your point.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by BeetleB on Friday March 25, 2022 @08:32PM (#30808263)
> I was hoping someone finally had a library or something to make python good at the things shell does well (mostly, running stuff and piping output around). If only.

xonsh (https://xon.sh/)

Been using it for a few years now. It's worth it.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by emacs28 on Saturday March 26, 2022 @01:23PM (#30813593)
I haven't heard of xonsh either till now. After looking it over, it seems like a perfect integration of sh with Python. There's a good chance I'll give an honest shot at migrating from zsh to xonsh this week.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by rvieira on Saturday March 26, 2022 @06:17AM (#30810632)
Never heard of xonsh before and it looks really useful. Thanks.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by GSGBen on Friday March 25, 2022 @11:37PM (#30809057)
> I was hoping someone finally had a library or something to make python good at the things shell does well (mostly, running stuff and piping output around). If only.

Good news, they did! Even better news: it doesn't require python, it's cross platform, and supports an easy-to-read verbose style for scripts but a quick-to-write terse style for interactive usage. It's an absolute joy to use for nested data structures, everything is an object and it has full native tab completion.

Unfortunately it came out of Microsoft so a lot of people haven't given it the time of day yet. It's honestly one of the joys of modern CLI usage though.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by derekbaker783 on Saturday March 26, 2022 @12:41AM (#30809280)
What is Powershell
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by DyslexicAtheist on Saturday March 26, 2022 @05:44AM (#30810503)
I just added powershell support to my Debian.

  $> ll /bin/powershell
  lrwxrwxrwx 1 root root 9 Mar 26 14:40 /bin/powershell -> /bin/dash

  $> cat ~/pwr_to_the_people.sh
  #!/bin/powershell

  echo "Hello Powershell"


  $> powershell ~/pwr_to_the_people.sh
  Hello Powershell
Works like a charm!
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by aloisdg on Saturday March 26, 2022 @06:24AM (#30810653)
A dotnet based shell where you can share around dotnet object instead of plain text.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by Xavdidtheshadow on Saturday March 26, 2022 @03:06AM (#30809888)
> obscure and difficult-to-predict

I mean, I think there's something to that. Bash has a lot of arcane rules; I personally never write an `if` statement right, what with the `[[` vs `[` and having to surround the condition with whitespace. I find that bash scripts carry a million little gotchas that will cause variables to not evaluate/expand the way you'd expect.

Say what you will about Python, but it's much easier to reason about what a script will do as compared to bash (unless the bash is _extremely_ simplistic).

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by hoistbypetard on Friday March 25, 2022 @09:53PM (#30808630)
> I was hoping someone finally had a library or something to make python good at the things shell does well (mostly, running stuff and piping output around). If only.

I've only kicked the tires on both, but plumbum:

https://plumbum.readthedocs.io/en/latest/

and sh: https://amoffat.github.io/sh/

both feel pretty good in that regard.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by mbreese on Friday March 25, 2022 @10:29PM (#30808777)
Even if you have a great library that abstracts away shell like things, you’d still probably be better served with a shell script. Shell scripts aren’t supposed to be big. They are quick one offs that solve immediate problems. This means that they are also spread around the filesystem. I’m of the opinion that one shouldn’t install libraries into the system Python path. I also tend to make heavy use of virtual envs. Setting up a venv for each off scripts doesn’t seem like a great plan.

For more centralized workflows, I could see Python shell replacements being helpful. Especially project specific scripts where the overhead of managing a venv is negligible.

But realistically replacing the majority of bash scripts? I just don’t see it.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by kimmeld on Friday March 25, 2022 @10:51PM (#30808865)
We have a tool that’s better at dealing with shell stuff. It’s called Perl.
Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by drewcoo on Friday March 25, 2022 @11:07PM (#30808930)
Is it Christmas already?

If not, there are copycats like Ruby and Python . . .

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by MichaelMoser123 on Saturday March 26, 2022 @12:08AM (#30809162)
https://github.com/MoserMichael/subb

https://pypi.org/project/subb/

I wrote this to simplify just that for my own tools; the subprocess module that comes with the batteries has a very general interface, i think that it is a bit complex for a quick script.

My objective was to get an abstraction, for a one line process run and extraction of the result, similar to what we had in Perl5 with the system library function. https://perldoc.perl.org/functions/system

Also the shell is impractical, when it comes to slightly more complex programs. There is a limit on what you can do with pipes. Maybe that's the reason why perl is that flexible, as they tried to bridge both realms: Perl had to be useful as a replacement for the quick shell like script, and to be useful as a general purpose programming language.

Re:Bashing the Bash – Replacing Shell Scripts with Python (2... by andrewshadura on Friday March 25, 2022 @08:58PM (#30808393)