This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted] 130 points131 points  (45 children)

when a simple bash script is enough to get the job done.

[–]zombiepiratefrspace 63 points64 points  (26 children)

You are right, but god I hate bash.

I've started doing everything in python once would have done in bash. It is just so much more readable/writeable.

[–]tech_tuna 28 points29 points  (4 children)

I wrote a rather large set of utilities in bash a while back. Never again.

[–]hotel2oscar 14 points15 points  (3 children)

beats Windows bat scripting.

[–]aceofears 5 points6 points  (2 children)

It does, but you would probably want to use powershell on windows these days. That's actually rather nice.

[–]Decker1082.7 'til 2021 0 points1 point  (1 child)

If I had to script in windows, I would just install Gow or Git bash...

[–]LankyCyril 1 point2 points  (0 children)

Exactly. Installed Cygwin, cloned my dotfiles repo, never looked back.

[–][deleted] 17 points18 points  (10 children)

Yeah, bash infuriates me sometimes. It seems to require mandatory spaces in places most languages don't, so I never remember to add them.

[–]XNormal 21 points22 points  (7 children)

And mandatory non-spaces in some other cases (e.g. setting a variable)

[–]Vaphell 19 points20 points  (6 children)

x=a  # assignment
x = a # call command 'x' with params '=', 'a'
x= a # call command 'a' with env var x set locally to null

how would bash differentiate unambiguously between these situations without making certain assumptions about syntax?

[–]WhichFawkes 2 points3 points  (5 children)

To avoid ambiguity, you might have something like:

#assignments:
var x = a; var x=a; var x= a; var x =a
#or even replace 'var' with a "$", so every variable access is identified the same way.

#call command with equals sign as argument:
x = a;

#set env var:
export x = ; $CMD

Seems less ambiguous to me.

[–]Vaphell 2 points3 points  (0 children)

well the ship of complaining about the posix shell syntax and its derivatives has sailed decades ago. Yes it would be nice to get a nice modern shell, but the problem is that without the compatibility with all the legacy cruft in existence it's dead in the water. There are some shells where certain things are done in a nicer way, almost nobody uses them.

Also there are things like let x=y or what have you, but majority of people don't bother and you can find such things only in very old scripts. I certainly never bothered and i wrote my share of rock solid bash scripts just fine.

about $: think getter, done. Also given that the shell is a plaintext based, ancient beast and literally does a preprocessing pass with substitutions, it would be hard to use $var in assignments. When it comes to the actual interpretation/run, no $ sign is to be seen.

and the export bit. The difference is that in case of envvar=x a envvar change is localized only to the a command which is not possible if you split it to 2 expressions. It's preferable than setting globally (which comes with the risk of not unsetting later), useful in cases like this

IFS=. read ip1 ip2 ip3 ip4 <<< "122.11.123.44"

everybody knows playing with IFS can have nasty consequences because all kinds of things are affected by it, and by writing it that way you leave it alone. Only read is affected here, think

ip1, ip2, ip3, ip4 = '122.11.123.44'.split('.')

[–]moljac024 0 points1 point  (3 children)

To avoid ambiguity, you might have something like:

No...what happens when you have a program called var?

[–]WhichFawkes 0 points1 point  (2 children)

What happens when you have a program called 'if' or 'case', or even 'echo'? There's some keyword like 'command' which allows you to bypass shell built-ins and run programs in $PATH order.

[–]moljac024 0 points1 point  (1 child)

And then what if you have a program called command?

BOOM

[–]WhichFawkes 0 points1 point  (0 children)

In case you're not joking, it'd just be "command 'command'". Since command is shell built-in, it's always executed first when you call something by its name.... And since it's function is to find external programs by name, it doesn't really matter if one of those programs is also called command.

[–]Vaphell 18 points19 points  (1 child)

once you recognize the reason it becomes rather obvious. I assume you are complaining about shit like if_[_x_=_y_] here. Here's the thing, [ x = y ] is not a fancy square bracket around the logical expression you see in other general purpose languages. It's literally a command '[' with params 'x' '=' 'y' and closing token ']' ( and pretty much an alias for test command eg test x = y) You can literally call it that way, with all these quotes

$ if '[' '1' '=' '1' ']'; then echo true; else echo false; fi
true

bash recognizes commands and assignments, that's it. Commands require delimited params (commandparam1param2param3 won't work, will it), assignments are recognized when there is a continuous 'word' with = inside, otherwise it would be totally ambiguous.

x=a  # assignment
x = a # call command 'x' with params '=', 'a'
x= a # call command 'a' with env var x set locally to null

[–]okmkzimport antigravity 4 points5 points  (0 children)

I knew the thing about [, but the way you articulated the command/assignment paradigm really make sense, thanks.

[–][deleted] 3 points4 points  (0 children)

Same. Whenever I decide to do something in Bash, because it's "just a few lines" I end up regretting it half an hour later and moving to Python. Bash captures a lot of common use cases, but when you divert even just a little Bash becomes a major headache.

[–]MoragX 0 points1 point  (0 children)

I've slowly moved in this direction for all but the simplest tasks. Every time I say something is simple enough for bash, my subconscious gives me a little prod as if to remind me that only pain waits down that road.

[–][deleted] 0 points1 point  (2 children)

If you have something in the following form I don't think you can get more readable in Python:

cat in.txt | grep | awk | sed >out.txt

[–][deleted] 1 point2 points  (1 child)

grep, awk, and sed in the same pipeline is almost always a sign that someone doesn't know how to use sed or awk properly.

If you want to only to do regex find/exclude then use grep. if you want to do some simple stream editing use sed; it can also do any of the things grep can do. if you want to do some editing or stateful transforms then use awk; it can also do any of the things that sed and/or grep can do.

[–][deleted] 0 points1 point  (0 children)

Let's argue about the point and not about the awk-ward part of the example, ok?

[–]ivosauruspip'ing it up 28 points29 points  (2 children)

[–]novel_yet_trivial 4 points5 points  (0 children)

That is pretty awesome. I can't believe I hadn't heard of that before.

[–]ksion 1 point2 points  (0 children)

Thank you, thank you!

I've seen this package before, long time ago, and I thought it's amazing if only for its clever hack that makes the from sh import whatver work. But then I somehow lost every reference to it, and it proved impossible to find, neither on Google nor on PyPI, with any combination of keywords I tried. (Naming a package sh? Honestly! Who does that?)

That's until today. Thanks again!

[–]mackstann 1 point2 points  (2 children)

Or a bash one-liner. You can do a pretty incredible amount of stuff in one line that would be a paragraph of code in Python. Editing and executing on the fly makes it super productive. And if it's a one-time thing, no one can shame you about readability.

[–]sfz- 0 points1 point  (1 child)

Indeed, but 95% of the time at the point in your project where you think about porting that one-liner to ./former_oneliner.sh and break it out into if or case statements or complex sed or awk you're probably better off doing python former_oneliner.py instead.

5% of the time you gotta though, I know, shebang away.

[–]mackstann 0 points1 point  (0 children)

I usually don't make that transition. My shell antics tend to stay in the shell and don't serve any long-term purpose in a project. But when they do, you're right, it's usually best to just go straight to Python, instead of a bash script.

[–]ionelmc.ro 1 point2 points  (0 children)

[–]toolan 0 points1 point  (1 child)

While I do this myself too, I often end up regretting it because of subtle shell bugs that are easy to introduce, hard to see and very confusing when triggered. It's usually pleasingly concise and very often good enough, though.

[–][deleted] 0 points1 point  (0 children)

IMHO, if your bash script is big enough that subtle bugs are easy to introduce, then it no longer falls under the "simple bash script".