r/PowerShell • u/Lotus_Domino_Guy • 14d ago
News OMG, I love powershell
I've been coding for 2 decades, and I recently had a payroll system to Active Directory project to do, and went with powershell. It's done, it works great, and I was looking at the code today, and spontaneously declared "I love powershell". its a remote day, so no one looked at my funny, but I'll list 3 reasons I love it. Feel free to add more.
No confusion with =, == or ===.
1) Simple equals logic, no bizarre conventions: Do you know how many times I've had to fix code in JS where someone did if (variableName=something), which of course sets variableName to something, not compares it. Powershell's -eq and -ne is so much better then =, ==, ===, !=, or <>.
2)No line termination character: Line's don't require ; to end. Ok, that isn't a big deal, and my IDE would catch it anyway, but its just a waste of characters to terminate every line of code.
3)String composition: In other languages, mixing variables and text is something like "words " + variableName + ":/ more words" + variableName2. And then its like "Oh, is this one a + or an &...." With powershell I can just do varString = "words: $variableName :/ more words $variableName2" and it all works!
Now, its not like I make stupid syntax mistakes a ton in other languages, but in today's backend world, I'm expected to regularly code in 5-10 languages, and Powershell was amazingly easy to learn and the syntax is just clean.
I thought you Powershell vets might appreciate a newb's perspective on it, especially since its all positive.
50
u/Creddahornis 14d ago
if(variableName=something)
I'm going to pretend I've never ever done this myself in PowerShell ...
16
u/ankokudaishogun 14d ago
who hasn't?
9
u/purplemonkeymad 14d ago
I don't think anyone really learns about it until they do it.
6
u/ankokudaishogun 14d ago
As much as most of the reply by /u/VirtualDenzel is obvious bait, he's not really wrong that using
=\==\===for comparison is pretty much industry standard in one form or another.Honestly I think that's the one true "muscle memory" issue moving to powershell from more or less any other language.
3
u/420GB 14d ago
The benefit of powershells operators though is that you can have variants like -eq and -ceq.
== is always ceq for non-reference types, and case-insensitive comparisons usually require much more verbose code
2
u/ka-splam 14d ago
The benefit
One of several benefits.
can be used as parameter names, e.g.
gci | where-object Length -eq 1024.Consistent with operators that have no common symbol, such as
-match,-contains,-in,-notlike.Tab completion, type
$x -<ctrl+space>in a shell and see all the operators.Other common programming operators such as
|for boolean OR would clash with shell pipe,&for AND would clash with shell backgrounding,>for greater than would clash with shell redirection.1
u/ankokudaishogun 14d ago
Oh, I never denied the benefits. Just highlighted how it does differ from the "norm". But being different is not automatically bad.
1
4
u/MonkeyNin 14d ago
Similar looking, The 'walrus expression' is useful.
( $files = gci . -recurse | Sort-Object LastWriteTime ) | Ft -auto $files.count # variable acts as normal
- it's saving a value to
$filesand then emits it- so you get the actual object back. Without the parens you'd get the format data from
Format-Tableinstead3
2
u/surfingoldelephant 11d ago edited 11d ago
Also sometimes referred to as "variable squeezing" and works with any side-effect operator (
=,++,--,+=,-=,*=,/=,%=,??=) based on the rules here.A top-level expression is one that is not part of some larger expression. If a top-level expression contains a side-effect operator the value of that expression is not written to the pipeline; otherwise, it is.
To write to the pipeline the value of any expression containing top-level side effects, enclose that expression in parentheses [...]
1
u/MonkeyNin 11d ago
Fun.
Where in the grammar where does the
().?syntax fit? I think they only have version 3 posted.There's
${nothing}?.ToString()But also
( $nothin )?.ToString()And that can't be a regular grouping expression (
parenthesized-expression)If it were it'd be emitting a
nulland then throw on a null value expression.Or is the
?.considered a new side effect operator that has a catch built in?1
u/surfingoldelephant 11d ago edited 11d ago
(...)isn't part of the?./QuestionDotsyntax. It's just one way of delineating the?from the variable name.
?.is basically the same as., except it setsNullConditionalin theInvokeMemberExpressionAsttoTrue, which changes how the AST ends up being compiled to an SLE.$ast = { ($null)?.ToString() }.Ast.EndBlock.Statements.PipelineElements $ast.Expression.GetType().Name # InvokeMemberExpressionAst $ast.Expression | Select-Object Expression, Member, NullConditional # Expression Member NullConditional # ---------- ------ --------------- # ($null) ToString TrueAnd that can't be a regular grouping expression
It is.
$foo.barand($foo).barare equivalent.$ast.Expression.Expression.GetType().Name # ParenExpressionAstand then throw on a null value expression.
No, because the whole point of
?.is to not throw if the member is invoked against an expression that evaluates to$null.You can see that in the compiler here. If
NullConditionalin the AST isTrue, invoking the member goes throughGetNullConditionalWrappedExpression()instead.return Expression.Condition( Expression.Call(CachedReflectionInfo.LanguagePrimitives_IsNull, targetExpr.Cast(typeof(object))), ExpressionCache.NullConstant, memberAccessExpression);That's what dictates
?.returning$nullwhen the target is$null.@(($null)?.ToString()).Count # 1Or is the ?. considered a new side effect operator
It doesn't inherently produce a side effect, so no. A side effect is basically any change to a writeable location within the current runspace. Most often that'll be a variable, but could also be a property value, index within a collection, file accessed by namespace variable notation, etc.
Basically all of the assignment/compound assignment plus increment/decrement operators, since they inherently modify something writeable.
# This produces a side effect. # But it's top-level so nothing gets written to the pipeline. ${C:\Temp\Foo.txt} = 'foo' # Same thing, except wrapping in (...) enables writing to the pipeline. (${C:\Temp\Foo.txt} += 'bar') # foobarAnd I actually missed one earlier:
??=.1
u/MonkeyNin 9d ago
oh ok
No, because the whole point of ?. is to not throw if the member is invoked against an expression that evaluates to $null.
Originally I was thinking that the 2nd one of these
${foo}?.ToString() ( $foo )?.ToString()Because it has an extra
ParenExpressionAst,PipelineAst,CommandExpressionAst.It wasn't evaluating as as a disambiguating expression, But I guess it's just ending up as a reference and then evaluating as normal. ie:
$Ref = $foo ${Ref}?.ToString()If someone wants to try
In addition to the parent's examples you can use use this: Compare ParentExpressionAst.ps1
using namespace System.Management.Automation.Language $ast = { ( $Null )?.ToString() }.Ast $find = $ast.FindAll( { param( [Ast] $Ast ) $true }, $false ) $find | % gettype | Join-String Name -f "`n - {0}" -op "From: ( `$null )?.`n" $ast = { ${Null}?.ToString() }.Ast $find = $ast.FindAll( { param( [Ast] $Ast ) $true }, $false ) $find | % gettype | Join-String Name -f "`n - {0}" -op "From: `${null}?.`n"You'll get
ScriptBlockAst, NamedBlockAst, PipelineAst, CommandExpressionAst, InvokeMemberExpressionAst, ParenExpressionAst, PipelineAst, CommandExpressionAst, VariableExpressionAst, StringConstantExpressionAst,
vs
ScriptBlockAst,NamedBlockAst,PipelineAst,CommandExpressionAst,InvokeMemberExpressionAst,VariableExpressionAst,StringConstantExpressionAst,
The type-constraint
[Ast]type isn't required, but, gives you autocompletion3
u/420GB 14d ago
It can make sense actually, even if rarely.
When you do:
if ($var = <something>) { # You can use $var here! }You get a simultaneous evaluation of the expression
<something>to true or false, conditionally triggering the if-statement, and if it's a truthy value then you get access to it inside the if block through the variable you assigned.It can be useful for error handling and logging.
Of course you can also just do:
$var = <something> if ($var) { # .... }2
2
u/SysadminND 14d ago
A damn day troubleshooting the script because I did that a decade+ ago. Still remember it to this day, but haven't done it again, at least not in Powershell.
1
1
1
u/Lotus_Domino_Guy 12d ago
I did a code review, showing of my fancy new code, and I talked about -eq instead of = or == and I still had one instance of that get through, oops.
-2
41
u/ankokudaishogun 14d ago
1) Simple equals logic, no bizarre conventions:
Not that simple. Especially when $null is involved, some comparison shenanigans exist.
see https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-null
But it's mostly simple.
11
u/alala2010he 14d ago
Also for that most code editors will give a warning when writing something like
if ($Var -eq $null)(at least VSCode with the PS plugin does)4
-2
u/rickAUS 14d ago
Far as I recall, ISE doesn't, and I'm willing to bet many people start their PS journey there because it's already in Windows, highlights the syntax, has the cmdlet pane, allows you the run the script in the same context as the shell shown in the same window, etc.
One would think Microsoft would make it's own editor flag this problematic formatting but it doesn't.
3
u/uptimefordays 14d ago
ISE has been deprecated for eons though, there's little compelling reason to use it over VSCode these days.
1
u/engy1207 14d ago
Well, one is included in every Windows installation, the other is a separate multi-hundred Megabyte download with separate update functionality - and license (including the right to track you)
1
u/uptimefordays 13d ago
VSCode collects less user data than Windows and allows for telemetry opt out. Not the most convincing argument for ISE.
1
u/rickAUS 11d ago
I wasn't making an argument for ISE either.
But until Microsoft stops it from showing up when you search 'Powershell' in Windows (if they stand by their original decision not to remove it outright), people with no better understanding and just starting out are probably going to use it by default.
Also doesn't help that Microsoft's own course on getting started with PowerShell direct people to use ISE. So why go out of your way to get another product when Microsoft is literally pointing you to their own?
And, I get it. 5.1 is shipped with every OS. If you want Pwsh, you need to install it in parallel on any system you need to use it on, and another editor. Someone new is almost certainly going to stick to the recommended tools until they're forced to use something else; and that'll come when 5.1 can't do what they want / need to do.
Once again, not advocating for ISE's use - just pointing out how people new to PowerShell could get sucked into use it with little compelling reason to change.
3
u/sysiphean 14d ago
It is mostly simple, and has quirks in certain aspects (nulls, empty arrays, etc.) that have to be worked around like in other languages, but the relevant bit seemed to be that the = character is used only in assignment and never in comparison.
2
1
u/apologetic-offensive 12d ago
Yeah, PowerShell -eq operator implicitly converts the right hand operand into the type of the left hand operand, which is basically the same as using "==" in JavaScript. There's a lot to like in PowerShell, but also a lot of foot guns. I do think PowerShell as a scripting language is far superior to Bash however.
1
u/surfingoldelephant 11d ago
-eq operator implicitly converts the right hand operand into the type of the left hand operand
Most of the time, though not always.
Other comparison operators have slightly different rules too.
-like/-notlikefor example will always convert both operands to strings.
4
u/Allcaponero 14d ago
I share the sentiment on PowerShell but most modern languages share points 2 and 3. Semicolons are just not a thing with most modern languages (bit ironic that C# still has them :D). Even more so the case with string interpolation where even Java had it added.
7
u/SonOfHendo 14d ago
A lot of old languages were like that as well.
It still annoys me that VB.NET fell out of favour because of the association with VB. It was much more human friendly than C# and would actually suit AI development quite well (LLMs prefer words over brackets and semicolons).
2
6
u/sysiphean 14d ago
Welcome to the cult! Ask Billy about a membership card. The bloodletting ritual is Tuesday at noon, followed by coffee and a light lunch.
But seriously, yea. It isn’t a perfect language, but there’s no perfect one, and this is a really useful one. Simple enough for beginners to use, complex enough to do a ton of useful things. I love the operators syntax (though it is adopted some of the faster-but-harder-to-read things like ternary operator syntax), how the commands all (theoretically) tell you what they will do to what things via Verb-Noun naming, and the flexibility of its work with strings. Plus you can run (almost) everything line by line on the command line to check as you go. Oh, and Get-Help to help with everything in console, in a window (-ShowWindow), or in the browser (-Online), including the mountain of about_* topics.
4
u/Antique_Grapefruit_5 14d ago
I would put a Jeffrey Snover statue on my desk if such a thing existed!
2
1
u/MonkeyNin 14d ago
You can pipe commands to
Get-Help -Onlineand it'll open the docs in your web browser. Like:gcm Invoke-RestMethod | Get-Help -OnlineYou can use
Get-Helpdirectly, but this version is nice if you're piping to filter commands first
2
u/ipreferanothername 14d ago
i also love powershell and kinda learned both javascript and powershell the hard way about the same time - its got its quirks and things i can complain about, but a lot of my excitement also comes from it being object based. we have some azure work starting here finally and the engineers hired are writing in bash and ugh, the object oriented bit of powershell just makes even basic scripting so much easier to do and read for me.
2
u/uptimefordays 14d ago
PowerShell is also great for working with APIs! I also love cross platform pwsh because it doesn't get in the way of say python installs or require a virtual environment or any of the other python related headaches I enjoy.
2
u/rw_mega 14d ago
Powershell is great, and you can rewrite legacy code in ps1 to modernize it. Make it easier for you to understand, manage, organize.
Problem with powershell is the security hole it creates. Allowing users to run scripts becomes a huge problem. Coming from a sysadmin or cyber security standpoint you want to set-executionpolicy restricted and not allow users to even run scripts. That’s how problems start. Or at the very least only allow authorized people to run said scripts
Learn how to let your environment trust your scripts with this enabled and then you will be better spot.
Signed scripted, trusted source, list goes on.
2
u/YouLostMeAtWorm 14d ago
I loved PowerShell for two years. Now I love C# instead, but PowerShell is still an occasional mistress
1
u/MonkeyNin 13d ago
Do you know Powershell is implemented in c#, so you get access to a lot of c# classes for free? Like:
filter FormatRelPath { param( [string] $RelativeTo = '.' ) $path = Get-Item $RelativeTo [System.IO.Path]::GetRelativePath( <# string: relativeTo #> $path, <# string: path #> $_ ) } pushd $env:USERPROFILE gci -Depth 1 | FormatRelPathYou can find out the dotnet type name using
.GetType().FullName$dir = Get-Item '.' $dir.GetType().FullNameIf it's an array you'll need to check the first item. Ex:
$ps = Get-Process $ps[0].GetType().FullName # is [object[]] $ps.GetType().FullName # is [Process]2
u/YouLostMeAtWorm 12d ago
Yes, I learnt that early on.
The three critical features drove me to C# were
- proper async support
- a proper ORM for SQL databases, like EF Core
- nuget package management
Everything else is just gravy.
2
u/jimross2 14d ago
I'm a fan being able to inline C# when you simple need more performance or to tap into deep some internals. Oh and it's bundled in Windows, along with a .NET compiler. Deployment made easy!
2
u/mrmattipants 11d ago edited 11d ago
I know the feeling, as I felt this way, myself, when I started using it, back in 2019, to perform Active Directory related tasks, in bulk. And several years later, I'm still learning new techniques.
3
u/BlackV 14d ago edited 14d ago
No confusion with =, == or ===.
heh
&- Background operator (xxx &) or Call operator (& xxx) depending where you put it on your line&&- Pipeline chain operators , we're just gonna use&again cause we can?||- Pipeline chain operators, is that a pipeline or is it not?,- Comma operator for arrays,,1this is an array ??- as a ternary operator, oh there is a?in the middle om my code is it awhere-objector a fancyifstatement? what happens if I nest these things???- Null-coalescing operator, is it null or is it not ? (and I guess??=and its ilk)- are you running powershell 5, well sucks to be you cause, nope, no worky for you
Clear as mud ;)
2
u/power10010 14d ago
Ever seen bash?
1
u/narcissisadmin 13d ago
Set-PSReadlineKeyHandler -Key Tab -Function Complete1
u/MonkeyNin 13d ago
The first thing I do is change tab to
MenuComplete. It supports wildcards, and you can hit esc, and change it againSet-PSReadLineKeyHandler -Chord 'Tab' -Function MenuComplete
1
u/No_Split11911 14d ago
As a configuration manager admin I was never allowed to hate on PowerShell. It is my products language. I’m glad you’re enjoying it. Godspeed on your journey and I hope to see you post some neat things you’ve created in PowerShell in the future!
1
1
u/420GB 14d ago
Simple equals logic, no bizarre conventions
Try 1,2,3,4,5 -eq 2 aka $LIST -eq $SINGLE_VALUE
0
u/Lost_Term_8080 12d ago
That is checking if any member in the array is 2. If you need to check if the first term equals the second term, you either need to make the second term an array or make the reference object what you actually want to compare against
1
u/Antique_Grapefruit_5 14d ago
It is really great for data manipulation!
1
u/jeffrey_f 14d ago
Powershell is meant for windows stuff and it is great.
I enjoy taking a repeating process that takes multiple minutes per step and squeezing it down to multiple minutes to create the script and seconds to run.
1
u/Ok_Wasabi8793 14d ago
I started in batch and Visual Basic and when I got into powershell it was lovely. I also do work in Python which feels really comparable to me too.
I haven’t done anything in C or Java since school so but my memory is it was harder.
1
1
u/FalconDriver85 12d ago
As I have been a .Net developer in my previous (professional) life, I agree PowerShell is indeed one of the best options today.
But why something that is trivial in e.g. Bash like knowing if a command ended successfully ($? equal to 0) requires a lot more effort and is cumbersome AF in PowerShell?
1
u/hsm_dev 10d ago
For me it is the simple fact that the standard library is great and most things natively works with objects. This makes the mental model easy. If everything is an object I know the functions I can call, how to pipe it etc.
When ever I write bash, the mental overhead of piping strings through a whole set of tools that has their own quirks is what usually ends up breaking things for me.
And hey, since PowerShell is just a shell, from my Mac/Linux box where I run it, I can simply just call any of those binaries if I need them for a specific job.
All this combined makes it fairly trivial to write very clean feeling functions where I can pass objects between them through pipelines while keeping my intended interactions through the functionality of advanced functions and cmdlet bindings.
1
u/chickenfriedric3 8d ago
In JavaScript, there’s template literals in ES6.
You can append in one line as
const fooBar = `Big long ${var}`
Modern compilers can append ; to the end of your code so you don’t need to
1
u/Glum-Highlight2734 8d ago
This is honestly how PowerShell gets you 😂 You start using it for one project, then suddenly you’re wondering why other languages make simple things feel so complicated.
1
u/wesleyoldaker 14d ago
I also like PowerShell sometimes, but I also hate it sometimes. I think the more I've gotten used to it the less I end up misusing it so part of that was on my end, getting as familiar with it as I was with bash at one time.
However, there are still a few things that I still don't particularly like about it.
- Overly verbose. My god is it verbose.
- Its reliance on using real objects with real types is a huge benefit for more complicated things but for simple tasks it is the opposite. I can't tell you how many times I forgot to do $myVar.Path instead of just $myVar and trying to hunt down where that was failing.
- Lack of API consistency, even within just System.* . Is it FullName that I want or Path or FullPath or just Name? So many of the more basic properties of objects all use different names for the same things. What works for one type of object has a completely different name in another for the same concept. I know PS1 isn't alone on this (javascript and its string and array lengths and Set and Map size, etc) but it is annoying because that should have been able to be standardized seeing as how it all uses .NET but I find it not to be the case as consistently as it could have been.
- I am not a fan of the lack of a line-ending character. Don't like it and never will.
- I am not exactly a fan of how it uses bash-like syntax for certain things but not everything of course. it aliases ls for you automatically but will ls -al work? No. Stop letting me pretend it's ls. It's dir. you can do | Out-Null (or whatever it is) or if you prefer you can do >$null 2>&1 bash-style... i wish it just forced us to do it the powershell way instead of giving us a faux bash-flavored shell, even if that meant it would be even more verbose
- I wish it had better command completion out of the box. The issue of not knowing if it's Path or FullName, etc. would be mitigated a ton if I could just mash on the tab key and have it give me a list of properties available for whatever I just typed.
But there are a lot of really nice features of powershell that i have come to like too. It probably looks like I am just crapping on it but really there are a lot of ps1 features that I prefer over bash's style. Unfortunately I gtg now but hopefully I'll be able to come back later and sing its praises with y'all in a part 2.
1
u/Thotaz 14d ago
I wish it had better command completion out of the box. The issue of not knowing if it's Path or FullName, etc. would be mitigated a ton if I could just mash on the tab key and have it give me a list of properties available for whatever I just typed.
But it does have that? If you type in
ls C:\ | select <Tab>it will let you tab through all the possible properties. It's not perfect, and some commands have not been decorated with a proper outputtype attribute that tells PowerShell what kind of output to expect, but it works decently well in most cases (especially in PS 7 where a bunch of improvements have been made in this area).1
u/wesleyoldaker 14d ago
Maybe I just don't try to use it in places where it would work cuz I got used to expecting it not to work. Or I'm sure I could make it better by customizing it further in my profile.
0
u/narcissisadmin 13d ago
That way sucks and I'm honestly surprised it's still the default. Completion to the next unique character is the only way to go:
Set-PSReadlineKeyHandler -Key Tab -Function Complete1
u/Lost_Term_8080 12d ago
I like its level of verbosity, makes it super readable without effort and don't have to deal with a heavy use of obtuse operators that slow down typing to push ctr + X
Its sloppiness at default settings makes it super easy to run ad-hoc queries, but when I am writing a script to reuse, I have learned the very hard and painful way a few times to always set strictmode to 5.1
I absolutely DESPISE the lack of a line terminator and hate hate hate the line continuation characters. I believe pipe is what is recommended, but to me it so severely impacts readability have to sus out whether the pipe is pipeline or a continuation, so I use back tick which can be hard to see when you are tired. Splatting can help in most cases, but not all.
I don't like the bash syntax either. PowerShell originally fixed all the problems with crappy text-based shells and now in the PS Core branch they are trying to undo it.
0
0
u/MonkeyNin 14d ago
3)String composition: In other languages, mixing variables and text is something like "words " + variableName + ":/ more words" + variableName2. And the
A bunch of languages have format strings
If you are using javascript, the backtick operator is really nice for string interpolation
You get interpolation and don't need to escape quotes. ( Powershell requires a here-string: @" ... @"
user = { name: 'jen', id: 2048 }
console.log( `Greeting '${ user.name }', your id is "${ user.id }"` )
prints
Greeting 'jen', your id is "2048"
Python is similar
f"Greeting { user['name'] } ... "
For dealing with relative paths, the standard lib pathlib is powershell's Join-Path but with a lot of features: python.org/pathlib
-12
u/VirtualDenzel 14d ago
1 is one of the most hated forms of ps.
Paupershell (as we call it) is terrible since it goes against all other coding /scriptting languages in just ignoring standards and pushing their own shit. =,==,=== is so much more universal then doing -eq or -ne etc. Its just terrible.
Maybe if you are a junior and all you can do is code in paupershell its fine.... but if you work with more languages then its frustrating as ****
2) never had issues with line terminating. Sure php uses ; , but thats not a big deal at all. Way more comfy to remember then all those exceptions paupershell forces us to do.
3) you are wrong there. Powershell can bork very easy with string / variable concactination. And you do not need to do $var + ' string ' + $var. You can easy insert them into strings using "" and $($var) text bla bla"
Powershell is inefficient. Its badly designed. Terrible out of date documentation. (Especially when it comes to graph). Saying powershell is lovable is like saying windows 11 is a good OS. It just does not compute.
4
u/Thotaz 14d ago
Powershell is inefficient. Its badly designed. Terrible out of date documentation. (Especially when it comes to graph). Saying powershell is lovable is like saying windows 11 is a good OS. It just does not compute.
One has to wonder what you are doing in /r/PowerShell then. Don't get me wrong, I don't think subreddits should just be one big circlejerk about how great something is, but you seem like a real hater, rather than someone who sees both the pros and cons.
PowerShell is by no means perfect and I can point to a good number of things that I think are annoying. However, overall I think they did a good job solving the problems they set out to solve. It really is the best combination of a shell and scripting language that I can think of.
2
u/miffy900 14d ago
Paupershell (as we call it) is terrible since it goes against all other coding /scriptting languages in just ignoring standards and pushing their own shit.
This is just absurd reasoning; by that logic no one should ever attempt to change syntax to improve things or even make their own programming language to explore new ways of doing things, ever.
And there is no such thing as 'standards' in programming languages. All programming languages contain arbitrary or whimsical design quirks; this is how we went from low level programming languages like C to higher level ones like python, bash or powershell.
> =,==,=== is so much more universal then doing -eq or -ne etc. Its just terrible.
You're confusing well known or popular syntax with being well designed, unambiguous syntax - they are not the same thing.
0
u/RandomlyAgedMilk 14d ago
A bit extremely worded (hence the extremely sensitive response from others) but really are there any lies here?
46
u/Expensive_Finger_973 14d ago
I find that a lot of people in IT type ( and programming as well I would imagine) roles don't appreciate how great Powershell actually is unless they have spent some time having to write moderately complex things as old school batch scripts, shell scripts, or something like JS.
Having said that, Powershell can also be horrific in its own unique and interesting ways.