Jeffrey Snover and the Making of PowerShell
corecursive.com
corecursive.com
PowerShell faced extreme opposition at Microsoft, and its creator Jeffrey Snover was demoted for pursuing it.
Jeffrey was originally brought into Microsoft to help MS learn how to compete in the data center, but culturally they were so tied to the personal computer model of the world, that they fought him every step of the way.
Edit: Another interesting thing, is how Powershell exists because Windows isn't file based. Jeffrey's goal was server administration, but on Windows you can't just edit files to administer things, you need to call various APIs and get structured data back forth. The rich object model fell out of that. It was the only way.
( Also, apologies if the transcript has errors. I've gone from professional transcriptions to Descript and then a pass of GPT4 trying to find the right punctation breaks and then me doing a quick read through. I don't think its coming out as high quality as I'd like. )
For example, something like the below would be so simple for Microsoft to add to the product and remove a page of boilerplate code that I don't really understand well.
Create-Chart -Type "Bar" -XAxis $Cities -YAxis $GDP -OutputFile "C:/Documents/ProjectAnalysis/CitiesBarGraph.png"
There are probably users in the millions that are ok at the basics of programming, but don't have the job role to where tools like Java or C# make sense. Python is usually a good fit here, but I really wish Microsoft had something written for us common folks and not just server admins and IT folks.
If Microsoft put some more effort into PWSH to where it wasn't turtle slow at things like parsing files and then started adding things like what I talk about above. Maybe even cmdlets for statistics and science...it could be something pretty amazing that your typical business analyst could quickly use to build some really amazing software to do their job better or a prototype for the software team to actually implement in a more robust manner. It's such a really cool technology that has a lot of missing potential IMO.
It seems like Microsoft assumes that the three options are full software developer with C#, IT stuff with PWSH, or Excel for the business folks. Excel is really great in a lot of ways, but it is also pretty limited and VBA+Excel is one of the most limited ecosystems I've dealt with. I guess third party languages like Python, R, and so on make for another fourth option, but sometimes I wish Microsoft had spent more time in this area.
The TK GUI is indeed lightweight, but a bit antiquated. I was thinking of something closer to Rebol, but maybe with a GUI builder as building a GUI with just Rebol syntax (although crazy powerful - Tetris is less than a page of code in Rebol) is a little challenging.
The main thing though is neither TCL or PS or Rebol covers everything I think is needed in a modern business analyst programming language. You need a simple dynamic language, ease of sharing programs, reasonable performance, really good OS interop, ease of building GUI, and a very large ecosystem of tools. Python is by far the closest here and is the programming language of choice for those in this segment for a good reason.
I loved powershell with (some of) its weird warts, but I have moved on.
Say what you want about bash, at least it doesn't pull stunts like that.
https://flowingdata.com/2011/06/30/organizational-charts-in-...
(hey, I managed to sneak a rifle past HN's Unicode filter :-D)
It is as if DevDiv is now full into UNIX like, poliglot, FOSS culture and such, now under Azure business unit, whereas WinDev is back into how to sneak people into Windows licensing and the usual old culture.
> Say what you want about bash, at least it doesn’t pull stunts pile that.
I guess it depends on what you consider to be included in the terminal’s domain? There are entire papers and guides on which commands are considered safe (sometimes only when run in a very specific way), and which variants, alternatives, etc. you should use instead, for Bash scripts, because of the inconsistency in what a command evaluates, does, and returns for various distros. That’s not to hate on Bash, but just to point out that it’s not a strength of Bash vs PS.
Or the Powershell way where on can access data members directly.
I thought I would prefer the second method more, because the access looked much cleaner/structured.
But after all these years (still mostly Unix scripting but Powershell and some other environments too) my mind has changed.
Would like to hear what others would prefer' Unix method with some scripting language or windows method with Powershell)
And this is exactly the problem.
You no longer have en_US as a locale? Have some titles lounger.
You no longer have en locale at all? Have an greška instead of error.
Oh, you the schmuk who don't agrees to use the best units in the world, totally retarded and "bUt iF yoU WriTe THe dATe as in the journal..."[0] but freedom ones? No longer accept 13 as an hour. Like come on, every idiot knows there is no such thing as an 13th hour!
Oh, you added an additional column to help your busniessor whatever? Your sEd MagIK gone to hell.
Should I continue?
[0] when was the last time you actually wrote multiple documents so you can actually benefit from MMM-dd?
I'm not aware of any difference in bash between vendor distributions for which this is true.
In the past I've seen bad things happen because a script was written by someone on OSX that gets run on a Linux (GNU) based system.
Two common examples are the `sleep` and `sed` commands.
Edit: I meant to reply to the parent comment.
Monopolies always destroy innovation.
gcm | ? noun -like winget*
help Install-WinGetPackage
The "usual" winget cli tool is indeed not powershell compatible. But winget also ships with all the necessary cmdlets. You don't have to install anything extra.For instance https://github.com/microsoft/winget-cli/issues/3820 or https://github.com/microsoft/winget-cli/issues/3231.
Also, even though it does not completely work with PowerShell 5, it is marked as compatible. https://github.com/microsoft/winget-cli/issues/2881
The design choices made makes it weird. I hope it would be a good one in the future. But it is now problematic.
Install-Module -Name Microsoft.WinGet.ClientYes what I am doing can be done with intune but that's another story for another time. RIP Store for Business. I just want to deploy PowerBI and have it always be current and self updating.
I primarily write Powershell Core scripts for scenarios where I need to execute the same commands on a variety of operating systems, and I know that the script is likely to be maintained by your “typical” sysadmin (highly technical, but not a programmer)in an environment where installing runtimes for programming languages is discouraged. I switched to macOS as my daily driver about 2 years ago, so PS fits these scenarios pretty well, and Powershell Core updated fairly regularly. Sure there are annoying bugs and misses with the built-in and add-on MS modules: networking cmdlets are an almost total miss, Get-LocalGroup (and maybe other commands?) is totally broken on some AzureAD-joined machines, and the Azure and MgGraph Powershell modules still don’t have enough coverage to move on from the legacy Windows Powershell modules (or even to rely on just one of them, for areas they supposedly cover). But overall I’ve been pretty happy that 99% of the time I can write a powershell script once, and it will run on any machine with Powershell Core, in a consistent way.
There's still a lot of good stuff wrt powershell maintainability by normal humans (though the entire mental model of object output usually throws them for a loop for years) managing local stuff.
If you have a background in windows dev or any of those tools then yeah, easy mode go grab your tools, but for most people doing system automation the calling conventions and complete lack of discoverability within the powershell ecosystem (and their tools in no way helping out) made this not a realistic use case.
Shoot I have seen some powershell modules that have to embed string C#s and eval thing just to have basic performance or other basic use cases.
From the perspective of the Lisp Machine or, hell, even the AS/400, PowerShell (and, while we're here, the CLR) doesn't quite go deeply enough, pervasively enough, across the system to make it truly useful in the same way.
At the very least they could have helped with the discovery/usage problem, but that would probably have been a really tall order for one little language to do.
import xmlrpc
svr = xmlrpc.connect('http://remote/')
result = svr.add(1, 2)
The C# way is to use the Microsoft Windows Communication Foundation (WFC) Client Proxy using the Service Model Metadata Utility Tool and the Web Services Description Language (WSDL) and XML Schema Definition Language (XSD) files from the remote server, declare a public interface attributed as a Service Contract referencing a namespace, generate a class which inherits from the generic ClientBase<TChannel> and implements the new interface, create an instance of said WCF client and call its methods. (Or rely on Visual Studio magic to hide all that) - https://learn.microsoft.com/en-us/dotnet/framework/wcf/acces...In any decision, Python goes for "What would Guido do?" and C# gets some union of "what would a committee of Microsoft, IBM, Oracle do?", "What would impress Gartner?", "What is Microsoft legally obliged to do, and backwards-compatibly required to do?", "What would we do if we tried to do everything everyone needs all in one?", "What would Java do?", "What would a large team need to design and maintain a stable, typed, large system for years?".
PowerShell is on top of that; there's no simple included graphing and drawing, no simple hooks into Windows own voice recognition and OCR, and definitely not into whatever magically good ones newer Office / Cloud is using, no casual email or spreadsheet handling, no Visual Basic style form building, no simple data science; there's a few things you can do or download, generally less convenient than a Python equivalent. And Microsoft are leaving it all 'to the community' but the community is using Python so that's where the Excel power-user who wants to script a couple of things will go.
I have never desired to do xmlrpc, ocr, voice recognition, or gui building from my shell (and if I did I still don’t see how importing a C# library would be a big miss). What I do desire to do is open files, read their contents and pipe them into other programs, something that Python makes a pain to do with all the file handling. Powershell definitely excels at this, does that make Python a big miss?
in that context C# library isn't fit for it. Yes I agree Python and PowerShell aren't going for the same things; that's annoying because PowerShell is 80% of the way there.
> "I have never desired to do ocr, voice recognition, or gui building from my shell"
I have wanted those things. Windows which has a built in speech recognition engine which is COM automatable, a shell (PowerShell) which can be a COM client, and I have a folder full of phonecall recordings. I have a folder full of photos with things like menus and road signs and I want the equivalent of strings.exe for OCR and PowerShell could almost do it. I've wanted to build a Delphi or VB6 or C# style drag-drop GUI and tried to do it in PowerShell with SharpDevelop, WinForms code, the ShowUI module. I've wanted to build a TUI but Windows console host isn't good at those. I've wanted to get jpg metadata out and done it with Shell.Application automation around Explorer instead of downloading mediainfo.
All these thing have something in common - a core in a low level language, glued together or scripted in a high level language. Microsoft have written the core. They have written the high level language. They just didn't bother to make it all integrated for the ordinary power user, or flesh it out with more features along those lines over the years.
> "if I did I still don’t see how importing a C# library would be a big miss"
Because, compared to a builtin "ConvertFrom-Speech" you have to be enough of a programmer to know you need C#, go looking for a package, navigate oneget/winget/psget/nuget/github to download it, worry about .NET version compatibility and module paths, work out how to add an assembly, and then deal with interop, [ref] parameters, byte arrays, streaming.
> "What I do desire to do is open files, read their contents"
Same. And the contents could be all common formats on Windows since the 1990s - MP3, JPG, Excel - things Windows can read and play, things Explorer can read metadata from.
> "and pipe them into other programs, something that Python makes a pain to do with all the file handling. Powershell definitely excels at this, does that make Python a big miss?"
Yes, I think PowerShell is a far more convenient REPL than Python's REPL. Than any REPL I know of, actually - within the boundaries of introspecting small simple data, PowerShell and .NET at least. And yet Python has set()-set() and PowerShell has [system.collections.generic.hashset[psobject]]::new().ExceptWith() (it doesn't return anything it mutates in-place) and every week, people post on the internet asking how to do essentially set union, intersection, subtraction and equality checks in PowerShell and the answer has been unsatisfying for decades.
Worrying about version compatibility for new projects has stopped being an issue. The package either targets NS2.0 or whatever latest LTS currently is, in which case you just add its reference, or it doesn't in which case you use something else.
If it does, in 98% situations it just works. In the last 2% it has native dependencies which means either a) the package ships with binaries built for all popular platforms, b) the package adds a platform-specific dependent package automatically, or manually and mentions that in README (either with dotnet add package or system-wide library, apt-get install and friends), or c) the package comes with windows only native dll, which happens with ancient unmaintained packages, it's a rare case nowadays fortunately.
As someone whose primary PL is C#, I found https://github.com/waf/CSharpRepl and https://github.com/dotnet-script/dotnet-script far more accessible and useful. Compilation caching for the latter works relatively well to make startup latency tolerable for using it for writing scripts over Python. It's not the smoothest ride, but the advantages of C# make up for this.
Or I just do `dotnet new console -o MyScriptName --aot`, echo code into Program.cs and `dotnet publish -o .` it. Some do that with Rust as well. Especially useful if you need your script to go through a lot of data quickly and parallelize that well too.
"It would be nice if Microsoft polished the stuff they already ship so non-programmers could use it more easily"
"Well I'm a professional programmer (and I have installed a bunch of SDKs and tooling already) and I find all this trivial".
(and it was not that different from the UX above anyway, way better than e.g. Python)
But PS Core is a programming language, right? And only installed by default on Windows?
I'm assuming there's other constraints on your system that make it preferable to installing bash or python on your Windows boxes?
(There’s also the Az CLI tool, which I don’t like as much as the Az PowerShell module but might be easier to manage in an environment like that.)
You have Azure Powershel Cmdlets (the old 5.1 based, and the new Powershell Core based), AZ CLI (in Python), AZD CLI (in Go).
The only one that offers full power is actually the AZ CLI one, e.g. some Kubernetes features aren't exposed in the others.
[0] https://learn.microsoft.com/en-us/cli/azure/what-is-azure-cl...
I tried reading the transcript and couldn’t reach the end, it’s a bit hard to read in my opinion. I assumed it was machine generated while reading it, but I can’t say why specifically. Maybe it needs a bit of editing to be easier to read.
Thanks for the effort anyway, it’s still better than no transcript :)
Hard to get right, it seems.
I wondered why there were so many unintelligible passages. I found it laborious to decipher. Thank you for explaining.
That makes me sad. The dude is absolutely brilliant.
Awesome post!
What is the experience of other developers who are experts in both shells to compare them? Did PowerShell really fulfill the promise of being a more efficient and modern shell? Or people just use it because it is already installed and better than CMD?
http://mywiki.wooledge.org/BashPitfalls
I used Powershell recently and not having to wrangle with text (commands return objects) makes it a much easier scripting language, and command line language.
The fact that they have an official way to handle argument parsing is excellent, everything is unified and the commandline window is able to have autocompletion for literally every option of everything. Bash could never even dream of having that. It is incredible and makes you extremely productive.
But type coercion manages to introduce new ways to create bugs that Bash didn't have.
Honestly at this point I prefer PWSH but still kinda dislike both. I'm waiting for the new natural evolution.
This is a great list of pitfalls, absolutely, but they are more an argument for integrating shellcheck into your IDE than avoiding bash, IMHO!
I know, I know, possible religious war, but I find that bash+shellcheck is far more often the right toolset than switching languages to avoid the pitfalls.
The immense power and expressiveness and immediacy of bash makes so many programs and so much prototype->PoC->MVP progression so easy and effortless that it is worthwhile having tooling to catch the worst of the warts.
A 50-line awk script though? Get outta here with that lol
(and I don't recommend it for others because if we're looking at it realistically, most people who trust themselves shouldn't trust themselves)
Which language is that?
I know it ends up in the same terminology bucket for the public consciousness as "CLI stuff", but there is a separation and understanding this separation is crucial for writing good sh.
The problem actually lies in bash+coreutils being some sort of de-facto standard for command line stuff. The way bash+coreutils evolved was mostly for autotools and not for humans. This ecosystem could be much better.
PowerShell has more builtins, so it relies less on external commands, therefore it is less vulnerable to pitfalls due to mismatches between different programs.
But in the end really it doesn't matter in whose end of the kingdom the bugs come from. What matters is that's how people write Bash.
Try this exercise: make a .BAT invoke a powershell script that invokes another .BAT passing parameters containing double quotes inside. It just can't be done reliably. The .BAT is only just an example, any param passing to/from powershell (outside powershells internals) is a nightmare.
In bash, this kind of interaction is commonplace. You can make `find` generate shell snippets for you, and pipe the generate shell commands into another interpreter instance seamlessly.
Think of the sheer amount of software the uses the shell this way and you never notice. That npm script that just passes parameters along is relying on the shell interface, that CI yaml that passes variables is relying on the the shell interface, etc. It runs just for a few milliseconds, to pass and glue things around, super simple. Powershell is just not designed nor suitable for that.
The problem of an uniform interface _can_ be solved by changing how people write stuff. The problem of not fitting well as an architectural piece replacement is much more difficult to overcome. Powershell fits Windows though, but that's about it.
https://github.com/search?q=powershell+language%3ABatchfile&...
There's a need for a fast, nimble glue that powershell can't deliver on its own.
For example: find potential file duplicates in a folder, recursively, by grouping by file size:
Get-ChildItem -File -Recurse | Group-Object -Property Length | Where-Object { $_.Count -gt 1 } | Sort-Object -Property Count
No need to remember arcane `file` incantations, no need to parse textual output. You get real objects with properties, and tab completion can see their structure.Need to parse JSON? No need to involve `jq`, just `Get-Content -Raw whatever.json | ConvertFrom-Json` and do it directly from PowerShell. Need to convert XML into CSV? ConvertFrom-Xml (or Select-Xml) -> do stuff -> ConvertTo-Csv.
Is `Get-ChildItem` too much typing? Use `gci`, or `dir`, or `ls` (by default only on Windows). Is `Where-Object` too much? Try `where` or `?`. And things are case-insensitive.
... | Where-Object Count -gt 1 | ...
Or, of course, ... | ? count -gt 1 | ...There's a lot of nix aliases for Powershell commands. Get-ChildItem is also called with ls, mv calls Move-Item, cd calls Set-Location, etc. They made at least some effort to make it more accessible to people coming from *nix or cmd.
Also, unfortunately, you'll probably want to Update-Help first, because for reasons beyond my comprehension, PowerShell does not ship with detailed help installed by default.
Several years ago, I had to write a complex unattended robust data transfer system in PowerShell. (I know, I know, without more info these “requirements” beg many questions, but they are all out of scope for this reply.)
I enjoyed the experience so much I switched all my shells, on MacOS (my DD) and on Linux (my most common work environments) to PWSH.
What I liked most about it was the power of passing objects in pipelines, and being able to extract/manipulate some of the properties of an object in the first filter and still have access to others, along with those of objects created by that first filter, in filters later in the pipeline.
Immensely powerful.
The consistency of commands, of error handling, and of object properties was also very nice.
Eventually, as the nature of work changed I switched all shells back to bash, as the older muscle memory asserted itself. PWSH as a shell made sense when I was working and thinking so much in that space, but when I left it, it was more effortful to think in PWSH than to resume bash.
There are times I miss it. There is nothing else in the shell space that comes close, or at least not close enough to justify the effort of switching.
gci | select *, @{L='Hash'; E={ ($_ | Get-FileHash).Hash }}
Get-ChildItem |
Select-Object -Property FullName, CreationTime, Length,
@{Label='Hash'; Expression={ ($_ | get-filehash).Hash}}
but even then you could do what you want with a traditional loop and no pipeline: $results = foreach ($file in get-childitem) {
# a hashtable of things you want to be
# in each object ('row') of the output:
$data = @{
Name = $file.Name
SizeGB = $file.Length / 1GB
Hash = ($file | Get-FileHash).Hash
}
[pscustomobject]$data
}I feel like my PowerShell struggles are really about not knowing 2-5 core, general-purpose pipelining commands well enough to use in any situation.
Select-Object - Pick out specific fields from an object, create calculated fields etc.
Where-Object - Drop non-matching objects from the rest of the pipeline.
Group-Object - Cluster objects into groups based on a shared property value.
Sort-Object - Order an array of objects based on a property value.
Get-Content - Read from a file.
ConvertFrom-(CSV/JSON) - Parse a json/csv formatted string into a powershell object.
ConvertTo-(CSV/JSON) - Serialise a powershell object into a csv/json string.
(Parallel)ForEach-Object - Loop over each item in the pipeline, performing one or more actions on it. Usually occurs at the end of the pipeline, or when you need to call an executable that can't handle pipeline input.
One thing I struggled with in the beginning, was not knowing what properties an object might have. You can pipe any object into `Get-Member` and it'll list its available properties and methods.
Many of the "-Object" commands support the use of script blocks if you need to carry out more complex filtering/projection.
|? PropertyName -gt 5 # where-object filter numbers
|? PropertyName -match 'text' # where-object filter text
|? {$_.thing ...} # where-object filter script
|% PropertyName # same as select-object -expandproperty propertyname
|% { $_.thing } # foreach loop
|sort
|sort prop1, prop2
|sort {$_.Name.Substring(0,5) -as [int]} # sort calculated thing
|fl * # format-list , all properties
|ft -auto # format-table, autosize table column widths
|sv q # set-variable -name q, same as $q = <...>
|tee -var q # same but show the results as well as storing in qWhat I do remember is that until I understood what I was trying to do and why (mostly error handling around edge cases, all specific to the app), I couldn’t really grok the various object management calls, but that once I’d done a few rounds of PoCing and RTFMing, everything fell into place.
* Trying to be a .NET language. I don't know why the one-runtime-many-languages promise of .NET withered on the vine while it flowered in Java-land without the promise even having been made, but it did. If you're writing .NET code, use C#. (And I say that having worked in one of the very few teams doing large scale development in F#.)
* Failing to get the basics of being a shell right. I can't remember the details as I've left it firmly in the past now, but its handling of redirection was just broken, to the extent that things that were trivial in bash were nigh on impossible in powershell. I got the impression its developers ignored the things bash etc. did well in their enthusiasm over building something new and powerful.
* Being fetishistic about stuff like requiring Verb-Object naming for everything. It's certainly subjective and has its defenders, but IMO it renders scripts ugly, is awkward to type and doesn't materially improve discoverability or memorability at all.
Edit: And that isn't to say .NET doesn't need a scripting language. It sorely needs that and the various relatively useless variants on "C# scripting" are testament to that. Powershell isn't, and doesn't even attempt to be, a solution to that lack, though.
Just because something has persisted for a few decades does not make it good or worthy of being emulated on a platform different from the platform it started out on.
It feels like there is no more vision, desire and engineering excellence left in the teams responsible for it. I know that there are rare exceptions like Microsoft Store that, as an application, has started to work so much better because the person responsible for it cares, and it uses WinGet for updates and distribution, which is great. But these are droplets in the ocean.
At this point, setting up a Linux distro for a desktop yields incomparably better experience given equal amount of effort.
The only thing that makes me worried is that such a dismal state of affairs damages the image of .NET which is night and day compared to most other products made by MSFT in terms of quality and taste, it already needs help - note how much undeserved good will Go and Swift receive, despite former being much worse than everyone thinks once you look into the fine-print and implementation details, and latter having poor non-macOS support story and ecosystem outside of iOS development. And at the same time no matter the degree of improvement that happens to .NET, it is popular to bash it, even if the criticism is completely detached from reality.
(downvotes only demonstrate my point)
Why it's .Net is also covered and its probably not what you expect.
It's, in fact, pretty pervasive when interacting with Windows programmatically, just as it tends to be on Unix-alikes, and the fact that PowerShell doesn't do well to acknowledge that is frustrating. (Worse, Windows is a better Plan 9 of sorts with MIDL and COM everywhere, and PowerShell falls pretty flat here too compared to the experience of, say, slinging a dynamically generated command stream directly at fossilcons.)
Yes, I could break into C# or C++ for this, but that takes the tasks that rely on these operations firmly out of the hands of paraprogrammers.
(New-Object -ComObject InternetExplorer.Application).Visible = $trueThat said, powershell seems more capable than bash if you know it well, because it has a better type system and arguments are easier to mess with, whereas bash things are amorphous strings.
https://github.com/bionicles/tree_plus/blob/main/tests/more_...
That’s a slightly out of date version of what I use to provision stuff on my windows machine to test things, and can show what’s possible a bit.
It is very good of you want to work with Azure though. You probably don't want to use bash for that. But lots of reading up each time I use it. Bash is my go to for small jobs or a command line experience.
I am not an expert in either but have used both plot to get stuff done.
Windows... You have to wait until someone in WDG decides some existing idea is good and then also wait for them to get support to implement the idea, then wait to see how that whole thing goes. So yeah Powershell is great for interacting with Windows. But Windows thinks you're a dumb shit so you can only do what Windows already lets you do.
It was indispensable when it came out. VBscript and Cygwin were the only options; both of them sucked. The ability to use native .NET objects in scripts was insane; I definitely abused that!
Nowadays, Git Bash and WSL2 replace a lot of the value that PowerShell provided.
It's purpose was to drive Microsoft away from the GUI, and, at that, it more than succeeded.
I'd challenge the Cygwin aspect. With judicious use of cygpath & co., you can go VERY far with Cygwin. I've built an entire SaaS deployment system on top of it (and Python).
So, the experience of PowerShell on Windows is unbeatable... on Windows. It's easily beaten on any other system.
Unfortunately, it also has some real sore points for use as an interactive shell, and even some outright broken behavior. Some that I lament most frequently:
- Powershell has native tab-completion support, but the word-of-god dictated "Verb-Noun" command naming scheme means almost every command shares a common prefix with a slew of entirely unrelated commands, making tab-completion painful.
- Power shell has sparse built-in support for funtional style list manipulation or data structures beyond lists. There's a "map" command (%), but no reduce, zip, tail, etc. These feel like they'd fit in perfectly to the design of the language, but they're just missing. Also, pipes are lazy, but lists are always strict, which can cause friction.
- most operators are strangely named, such as "-eq" for ==, which hurts legibility.
- There're lots of unvoidable brackets, meaning you often need to jump around the line while writing out a command incrementally. For example, to get filenames, you either need to write `(Get-Item).Name` or `Get-Item | %{$_.Name}`. It'd be nice to have something like `Get-Item |. Name` as an option.
- It's impossible to pipe raw bytes between commands/executables; raw bytes are interpreted as strings. This means, for example, it's impossible to pipe a file to `patch`, because the contents will be decoded into strings, piped as strings, then re-encoded with line endings changed, which `patch` rightfully refuses to apply.
- Running Powershell scripts is disabled by default (WTF?)
By comparison, *sh's heavily favor interactive use. For example, you can put io redirections at the start or end of a line, tab-completion is often very good, it's often possible to avoid brackets (ex. with xargs), background/foreground job management, etc.
Overall, I often find Powershell more pleasant to use than *sh, especially when writing scripts. But for interactive use, I often wish Powershell was a little smarter and a little dumber at the same time.
Usually, the distinction between a single item, a flat list, and a list of lists isn't important in Powershell, because commands are written with Powershell's behavior in mind. It's extra painful when it does matter, but it's a trade off against being more verbose every time it doesn't matter. Ultimately, it's personal preference; for an interactive shell, I like the tradeoff Powershell made, and I think it's a cool design space to explore, but I do think there's a lot of room to improve the implentation of the concept to make it less surprising and less painful to choose the alternate behavior when needed.
Get-Item | % NamePut another way, you could feel free to parse the output of ls in powershell - because it's already been done for you.
I feel Bash is a 'glue language' in every sense of the word, and is optimised for interactive command-line usage because of its terseness. Being remotely productive on Bash presupposes the presence of an entire UNIX subsystem and coreutils (which is why Git Bash for Windows drags along an entire `/usr/bin` directory with all the coreutils in it). You can't really do much in it without the coreutils.
I find that Bash parseability and ease-of-understanding rapidly deteriorates in scripts of increasing complexity. After about a hundred lines of code I reach for other languages. I personally dislike its unintuitive syntax, global scope, and weak, dynamic typing. The terseness that lends itself to powerful interactive command-line usage means script readability plummets. Of course, that might just mean I'm inexperienced with respect to Bash and need more practice, but I find this a fairly common opinion amongst colleagues and friends. In my opinion, Bash scripts are write-only. It is a wonder that something like neofetch[1]—which is an eleven thousand-line monstrosity—lasted this long (if I recall correctly the author stopped maintaining it because—amongst other reasons—it had just become too complicated).
I find that people understand PowerShell better when they realise it is closer to Python than any interactive UNIX shell. By default, PowerShell is more verbose than Bash; for instance, the PowerShell equivalent of the two-character all-lowercase `ls` is the thirteen-character mixed-case `Get-ChildItem`. Arguments are by default also long and verbose, with things like `-FollowSymlink`. PowerShell is generally meant to be used in executable scripts, just like how most Python isn't executed in the interactive REPL, but by running scripts. Consequently, I find this verbosity lends itself to improved readability in scripts, at the expense of some interactive productivity. That being said, common PowerShell commands have UNIX-like aliases[2] (at one point the cause of much gnashing of teeth here and at /r/programming because PowerShell aliased `curl` to `Invoke-WebRequest`[3]) to improve interactive productivity. PowerShell also supports truncating parameter names; for instance, `Get-ChildItem -Recurse -Force` can be abbreviated to `gci -r -fo`, which looks remarkably like a UNIX command now. Even so, the PowerShell ISE and the PowerShell VS Code extension both suggest that programmers use the full unabbreviated commands and arguments in `.ps1` scripts[4].
PowerShell is also dynamically typed, though semi-static typing can be opted in to[5]. That said there is some controversy about arrays decaying to scalars when they have size 1 [6]. It is easy to set up both function and script parameters with as much power and expressiveness as `argparse` for Python, but natively without involving any additional modules[7].
The real power (pun not entirely unintended) of PowerShell comes when you realise it is just another front-end to the incredibly massive .NET ecosystem. Where Bash requires a full UNIX subsystem and a collection of hundreds of additional binaries to be productive, PowerShell employs pre-compiled cmdlets[8] written in .NET (usually C#) to augment the basic language. Most 'commands' in PowerShell are cmdlets, including `Get-ChildItem`. You can write your own 'back-end' in C#, F#, C++/CLI, VB, or any other .NET language, compile it, and expose a PowerShell interface for end-users. Pretty much like how people write fast but complex code in C/C++, compile it, and expose a Python interface (Numpy, Pandas, PyTorch, etc come to mind). You can additionally directly call .NET methods in PowerShell[9].
This also means that everything in PowerShell is an object. The output of `Get-ChildItem` is a list, not a string. Likewise for `Get-Process` (UNIX equivalent: `top`). These lists may be formatted[10], filtered, parsed, modified, dumped to JSON, and otherwise manipulated like any other list data type in most other languages.
PowerShell also lends itself to the side-effect-free/functional/monadic map-reduce paradigm very well, with pipelines[11].
In my opinion, it is not merely 'better than CMD'; it blows CMD out of the water. A little less so for Bash, because Bash is augmented by the UNIX core-utils. As should now be evident, in my view the real competition to PowerShell is Python.
[1]: https://github.com/dylanaraps/neofetch/blob/master/neofetch
[2]: https://learn.microsoft.com/en-us/powershell/scripting/learn...
[3]: https://news.ycombinator.com/item?id=12319670
[4]: https://learn.microsoft.com/en-gb/powershell/utility-modules...
[5]: https://stackoverflow.com/a/115815/1654223
[5]: https://superuser.com/a/414666/1132163
[7]: https://learn.microsoft.com/en-gb/powershell/module/microsof...
[8]: https://learn.microsoft.com/en-gb/powershell/scripting/power...
[9]: https://devblogs.microsoft.com/scripting/learn-how-to-use-ne...
[10]: https://learn.microsoft.com/en-us/powershell/module/microsof...
[11]: https://learn.microsoft.com/en-gb/powershell/module/microsof...
So if one invocation of your commandlet only calls `WriteObject` once and the other calls it twice, the shell in the first case doesn't have the knowledge that it should've wrapped that one output in an array too. And it can't always wrap commandlet outputs in arrays because that would be disruptive for commandlets that semantically only have one result and so always write only one output (like `Get-Date`).
And for whatever reason, they didn't want to complicate the API to let commandlets themselves be able to express whether they'll semantically write only one output or multiple, regardless of how many times they actually call `WriteObject`. Such an API can't be a static attribute on the commandlet because commandlets can have wildly varying output based on their parameters. It can't be an overload of `WriteObject` like `(Object, bool iMightWriteMoreValues)` because it has to work for empty arrays. So it would have to be a separate `IWillWriteMultipleValues()` function probably.
Edit: Also explained here: https://news.ycombinator.com/item?id=40874873
$Ary = @(, "value")
One cool feature for creating arrays (especially larger arrays, for performance improvements over using +=) is to assign your array to a for loop. It wasn’t an obvious method of populating an array to me, but is definitely handy. $Ary = foreach ($Obj in $Objs) { @{ foo = $Obj.foo } }I use
[array]$Ary = "value"That sounds like a classic case of software trying to be "too clever," nine times out of ten of which will invariably backfire.
As a software developer, one should always resist the temptation to make their software "too clever."
A scalar however is not a tuple of length 1
It's also way too verbose and slow for 90% of the stuff I'd use Bash for (or would have used Perl in another life)
I often wonder why Microsoft didn't base it on Python, Node, or something else. I can't remember when PS was first released so I'm not sure what would have been ideal at the time.
PowerShell is great because it's a swiss army knife that has a very nice REPL with autocomplete, no weird whitespace behavior, readline[0], and can do anything .NET can do if you need to do anything more complicated.
Plus it's object oriented so you can focus on doing the tasks you actually need to do rather than trying to figure out how to use archaic utilities to parse the text based output of other archaic utilities.
0: https://learn.microsoft.com/en-us/powershell/module/psreadli...
They originally based it loosely on Perl and the Korn shell. In the first edition of "Powershell in Action" by Bruce Payette there is a sidenote that states:
'PowerShell uses the "at" symbol ("@") in a few places, has $_ as a default variable, and uses "&" as the function call operator. These elements lead people to say that PowerShell looks like Perl. In fact, at one point, we were using Perl as a root language, and these elements stem from the period. Later on, the syntax was changed to align more with C#, but we kept these elements because they worked well. In Perl terminology, they contributed significantly to the "whipupitude quotient" of the language.'
It also states:
'The core PowerShell language is based on the POSIX 1003.2 grammar for the Korn shell. Originally, Perl idioms were appropriated for some of the more advanced concepts such as hash tables. However, as the project progressed, it became clear that aligning PowerShell syntax with C# was more appropriate.'
They probably didn't base it on anything else since they control .NET.
I doubt it's slow because of .NET, it's slow because of either its design or because of under-investment in performance.
However... which version of PS are you using? As far as I remember the newer versions are quite fast.
* wasn't source controlled
* was never tuned (properly) for performance
* deployed into environments by editing/executing sql in SSMS.
* of course, no automated tests, etc...
It's a windows shop, I am developing on a Mac, and we do linux on Github Actions.
I selected tools like PowerShell Core, sqlcmd, docker for running Windows SQL Server instances, RedGate SQL Compare for extracting existing schema and code from the legacy servers, tSQLt for unit testing, TSqlLint for code compliance, SQLFluff for style compliance, and Flyway for deployments.
We quickly discovered PowerShell Core was the most interoperable cross-platform scripting shell when Windows had to be one of the platforms.
It wasn't pleasant to code in. The regex engine comes from .Net, which has bad catastrophic backtracking problems, and the array situation was goofy. Launching executables with any sort of control over the launch and capturing the output stream was hit or miss - I would often have to launch a process, redirect its output to a temp file, and then read the temp file after the child exited. So piping around with child's stdout into a string variable was always more trouble than it should have been.
BUT, PowerShell Core executes fast (nice job M$FT, if there's one thing you do well, it's micro-optimizing!) It has nice tools for interacting with the user, like ascii art list pickers and easy input prompt generators. And, most of the strange quirks in dealing with the Windows file system are papered over if one avoids folders with locked files.
Anything you want to achieve can probably be done if you search hard enough. Recommended!
PowerShell however was actually pretty great to use, despite everything else on Windows being extremely clunky. It always felt very thoughtfully designed.
Linux is great, and I'll always use it as my daily driver for work, but using bash is absolutely horrible. But because it's always everywhere, it's something that everyone reaches for first, and so we'll probably still be dealing with bash scripts in 2100, warts and all.
The extreme verbosity of the syntax may look good for a presentation to the committee, but dealing with it regularly collides with well-researched and understood limits of the human brain, where information of a certain size, and delays of a certain length break the state of flow and requires concentration, explicit memorization and double-takes. Even with practice one could never quickly execute the common shell incantation to turn a thought into reality, instead one would have to wrestle with syntax, even if it's just waiting for autocomplete to appear and deciding to accept the next word of a multipart command.
Let's try the start menu and search for "pow..", chose between 4 amazing options, Powershell or Powershell ISE, both in regular and x86 flavour. Either would take a while to load, breaking flow. The ISE shows a little splash screen that jumps to a second location. Another dialog shows up and informs you that you've closed the last session without saving the unnamed script files. But it opens them anyway, as you would expect. So why scold me for this? Because, you know damn well that saving the textfile is trouble: I can type or copy, and then execute any kind of evil code imaginable, but saving the file and then running it as a .ps Script triggers the ridiculous execution-policy song and dance. Probably trauma from Microsoft's bad security reputation of early Internet Explorers and Windows versions.
I tried to love it anyway but one day my script encountered filenames with square brackets. Powershell implicitly interpreted those [1] and [2] as iterators somehow (https://stackoverflow.com/questions/21008180/copy-file-with-...). Sorry but dealing with files is the one job a scripting language has, filenames are beyond the control of script authors, and the space of valid filenames on Windows should be known. This gave me some long lasting trust issues with that language.
(The Azure team aparently had enough power within Microsoft to create their own, sane and readable syntax: "az find vm", "az account show".)
Um... no. Powershell was inspired by shell, Perl and a bunch of other languages, you see it in its design. The other part was just a desire for consistency, since *NIX knowledge is just brute forced. Yeah, -v is generally verbose and -h is generally help, but in practice you can't rely on anything.
In bash/unix, I feel -l often stands for list, -a for all, -f for force, -q quiet or slient, -r recursive, -d debug. A modern approach could have been for Microsoft to clean it up and make it more consistent. But no.
I just posted this elsewhere in the comments, but it does answer your question:
They originally based Powershell loosely on Perl and the Korn shell. In the first edition of "Powershell in Action" by Bruce Payette there is a sidenote that states:
'PowerShell uses the "at" symbol ("@") in a few places, has $_ as a default variable, and uses "&" as the function call operator. These elements lead people to say that PowerShell looks like Perl. In fact, at one point, we were using Perl as a root language, and these elements stem from the period. Later on, the syntax was changed to align more with C#, but we kept these elements because they worked well. In Perl terminology, they contributed significantly to the "whipupitude quotient" of the language.'
It also states:
'The core PowerShell language is based on the POSIX 1003.2 grammar for the Korn shell. Originally, Perl idioms were appropriated for some of the more advanced concepts such as hash tables. However, as the project progressed, it became clear that aligning PowerShell syntax with C# was more appropriate.'
> whipupitude
Historical roots and original intentions aside, the verbosity of Powershell syntax and the clumsiness of the shell place Perl and Powershell on opposite ends of the spectrum in terms of "looks" and the ability to quickly whip up something.
I completely agree.
I used Perl for many years and loved how quickly it could be used to hack together a quick solution to a problem.
I have been using PowerShell at home intermittently for the past few years and it feels much more clunky by comparison. It is nice to have a way to do automation on Windows (I have not tried it on other platforms), but the feel is very different.
Edit: maybe it was VMS, not sure.
There's an entire industry of consultants and software vendors that don't want that outcome.
> connecting with Remote Desktop and clicking around with a mouse
This generates a lot of hours.
> is horrendously difficult and annoying.
It is ironic that this is probably the main reason that alternative operating systems even exist.
But PowerShell? PowerShell's nice.
Your first paragraph creates expectation that the second paragraph disappoints, though. Would you explain why you think PowerShell is "nice"?
I remember watching a video on one of the various Microsoft learning sites in which Snover and another guy were interviewed about the creation of PowerShell. One of the two guys said that after they realized that UNIX-style wasn’t going to work they turned to VMS and drew a lot of inspiration from it.
Even Tab-path completion had to be done in an incompatible way.
2. Automatic introspection/command completion for command parameters, even user-created commands.
You can argue about a lot of other things Powershell does, but these 2 things are things which if Bash were designed today by 100 top notch software developers, would probably be part of 95 of their designs.
I've never been able to make a generally useful CLI tool in under a few thousand lines of messy code. You typically have to deal with: pipeline inputs, optional parameters, parameters with values, defaults with overrides, "dry run" mode, and the various output formatting requirements, and so on. You end up with 90% fluff and 10% action.
With PowerShell, a C# module is basically 20 lines or so of overhead, and the rest is all action. It's mindblowing how productive this is! You get parameter validation, parameter name tab-complete, pipeline input, pipeline output, formatting, strong typing, globbing, etc... all for free.
Just some of the stuff PowerShell did right:
- PowerShell cmdlets are self-describing and rich in information. Rather than each command doing its own parsing of parameters, cmdlets describe parameters and delegates the actual parsing to the shell. The shell understands data types, parsing rules, e.g. how to parse a UUID or a date. Not only does this ensure a consistency that was never in *sh shells, but it also enables cool stuff like e.g. autocomplete, predictive input, help instructions etc. almost for free.
- "Simulation" mode (-Confirm and -WhatIf) where a cmdlet can describe the action it is about to take, and the mode of the shell may decline everything (effectively a "simulation mode") or may actually ask the user for permission (-Conform) for each action.
But, alas, PowerShell never caught on outside Windows, and now MS is leaving it to wither in their quest to not upset a wider non-Windows community.
So in the end, PowerShell doesn't need to catch on.
But if you want a shell that makes it easy+quick to work with data in all kinds of formats, Nushell wins IMO.
For scripting, I don't get it. It's not designed to be a simple text-based glue like the bourne shell is, so it feels weird in many places (quoting, escaping, even more than sh is). It's very good for glueing Windows stuff though, like .NET libraries and so on.
E.g. 'wget' exists but run it on its own and it doesn't save the file to disk or print it out, it prints an object as a table, which is about the most useless output possible. I know why this happens but that doesn't make it helpful.
Reading about the dysfunction inside Microsoft makes it clear why it ended up this way though!
Want the result of wget in memory instead of print or disk? Write it to memory:
page_contents="$(wget -O- some_page)"
It doesn't try to apply some default structure, so I don't need to rely on MS doing a fancy special wget for me that calls thousands of lines Invoke-WebRequest in the background.A while ago I worked on a project to do pretty complex configuration on Windows machines. Everybody thought Powershell would be perfect for this. It turned into a complete nightmare with tons of oddities to work around. I myself barely can make sense of the code and I bet nobody else can.
Next time I would do either Python or write the whole thing in C#.
Executing C# in SQL Server: https://learn.microsoft.com/en-us/sql/language-extensions/ho...
An anecdote: about 7 years ago I made an postal code lookup function for SQL server, that would parse a csv from the danish postal services, that was retrieved from a HTTP GET in T-SQL, then parsed with C# to get the city from a postal code. It was a project i did for fun at school.
When I try it in PowerShell I get: Invoke-WebRequest : A parameter cannot be found that matches parameter name 'X'. and some more error messages
even curl -X "GET" doesn't work :-(
https://learn.microsoft.com/en-us/powershell/module/microsof...
That said, it did nothing to convince me to invest more mental energy in PowerShell. Every time I use it, I have the same “meh” reaction: The learning curve is too steep and the syntax is too ugly. It doesn’t ever “stick” with me, so I end up starting from zero every time I encounter it again.
I think the fundamental problem is that PWSH inhabits a dark valley between quick-and-dirty scripting and I’m-serious-about-this programming. It really needed to pick one side or the other to win people over, but never did.
These people at Microsoft knew that they’d wipe the floor with the professional services folks by getting that professional services cost out of the picture by providing the small UNIX-like toolbox to do sysadmin work via automations.
Of course, PowerShell being API-based instead of file-based was what came out of that.
It’s interesting because what has survived has essentially been Windows and UNIX (via Linux being able to drop all the proprietary baggage of UNIX solutions). Everything else that was built to sell professional services is dead.
Instead of uniting and having everyone collaborating into a common like Google with Android making it happen no matter what, or how Bell Labs tried with Inferno / Limbo, the active fight against .NET, and anything related.
To what ended up being the WinRT failure.
Ironically, WinDev is now shipping JavaScript and Webview2 all over the place on Windows 11.
I haven't checked back in a while, but I think most new features in PowerShell are just pointing back to the Graph API.
"I’ve never seen anybody use a GUI in a clever way. Ever. There’s no cleverness to it. No, like, Oh my God, you should see the way Adam clicked that mouse. Oh my God. Guys, guys, guys, guys, come on, check it out. Adam’s going to click the button. Oh my God. That’s amazing. It just doesn’t happen."
They are incredibly well built and feel first class/more polished than a lot of AWS tools.
for anyone who wants to try the newer version on Windows, get either Windows Terminal (from Microsoft Store or Github releases: https://learn.microsoft.com/en-us/windows/terminal/install) or Visual Studio Code. The classic Windows' command prompt console host engine just can't do Unicode and fonts and colours and Unix shell escape sequences.
After that, find something which will immediately trigger you to froth at the mouth, hurry onto some Microsoft forum and post about how Microsoft is the devil. Here's some popular choices, many of them valid complaints: ('curl' and 'wget' on Windows override the real programs with M$ imposters). (gci doesn't support the parameters of either dir or ls). (aliases work differently to Unix shells). (almost everything works differently to Bash). (gci -recurse is frustratingly slow). (it doesn't have a CLI text editor like nano). (you don't understand that GNU and Unix utilities aren't "Bash"). (PowerShell remoting with enter-pssession and invoke-command aren't SSH). (there isn't any sudo because Windows isn't Linux). (Execution policies are annoying). (it doesn't use UTF8 everywhere always). (backslash, the one true Unix escape character isn't PowerShell's escape character). (Line endings aren't Unix line endings). (having to use sigils to disambiguate between shell and code is worse than Python for coding and worse than Bash/cmd for shelling). (You want > to be both numeric comparison and/or Unix shell IO redirect and it's not). (you hate Verb-Noun and the one true way is Noun-Verb). (byte streams don't pipeline well or quickly). (It's verbose which you hate, but the elastic syntax one-liners are unreadable which you hate, it should have exactly the right amount of verbosity which coincidentally is exactly the amount you are comfortable with).
Moving on from there, avoid falling for the tempting usermode filesystem equivalent, which is abandoned and only exists for backwards compatibility making everything slower. Avoid falling for the declarative host config system Desired State Configuration (DSC) which is semi-abandoned and only hangs around for backwards compatibility. Control your enthusiasm about .NET/C# LINQ in a shell, because nope. Prepare yourself for the weirdness of a programming language which has shell style dynamic scoping instead of lexical scoping, shell style output handling where all output goes to the pipeline, pipeline obsessed array unrolling which spills the contents of containers all over the floor if you aren't paying attention, having to learn that there's more to output than just stdout and stderr and that the host and pipeline are different outputs, and that there's a lot of non-powershelly .NET and Windows stuff poking through everywhere. Prepare your armoured-toe boots for a large number of footguns and bugs in what is an intricate and complex shell/scripting language mashup.
Moving on from there, it's a REPL:
$x = 5
$x + 3
Numeric literals in hex and binary: 0xff
0b1010
Strings in single quotes are literal: 'foo $bar'
Strings in double quotes are not literal: $bar = 'hello'
"foo $bar $( 4 + 3 )"
It's a shell: ping google.com
It's introspective: gcm ping # get-command details of ping
gcm p* # commands starting with pi
help gcm
Function calls don't use () or "return" because shell-style usage and behaviour, variable names and function calls aren't case sensitive: PS C:\> function test($Left, $Right) { $left + $right }
PS C:\> test 4 6
10
PS C:\> test -Left 4 -Right 6 # named parameters
PS C:\> test -L<ctrl+space> # autocomplete
Reach for .NET libraries: PS C:\> [System.Ma<ctrl+space> # autocomplete to explore and find System.Math
PS C:\> [System.Math]::<ctrl+space> # autocomplete to find e.g. [System.Math]::Pow(4, 5)
Basic data types: $array = 1,2,3
$array | foreach { $_ }
$hash = @{KeyA = 4; KeyB = "b"}
$hash.keyC = 5
$hash['keyD'] = get-childitem
$x = 'KeyA'
$hash.$x # oooh
on Windows: gci | ogv # out-gridview
Text filter ("grep ish"): gc words.txt | sls '[iou]' # get-content and Select-String
Objects: ls | select Las<tab> # tab complete will pickup the available properties
# on the objects coming through the pipeline, where possible
ls | select LastWriteTime, FullName # two properties, kept separate, no text parsing
ls | % {$_.LastAccessTime.DayOfWeek} # the access time is a .NET System.DateTime,
# not a timestamp or text string.
ipcsv data.csv | select col1, col3 # import-csv, select-object
$hash = @{KeyA = 1; KeyB = "foo"}
[pscustomobject] $hash # casting
(Objects are used as containers for multiple properties and keeping them separate; it's not a full "object oriented programming with inheritance and interfaces" kind of shell/language, although it has some nods to that).