Furthermore, winget list will truncate long names (and IDs?) to fit the terminal width or to some arbitrary width when piped to a non-tty (or whatever the Windows equivalent of that is), which makes its output really terrible to use in scripts.
I guess this blog post illustrates how I still can’t get used to PowerShell. Author runs winget upgrade and, judging from the output, expects to be able to pipe this to SelectObject Id which fails because winget upgrade doesn’t produce PowerShell compatible output.
It seems to me that PowerShell users, by seeing a table in the output, automatically assume that there’s some underlying data model, where each row in that table can be converted to (or is?) an object in some way.
That means that when you run a PowerShell command, it will have invisible output that becomes visible once you pipe it through the right command. Me not knowing this magical command, will therefore not have access to information that is in an object right in front of me. It constantly makes me wonder if I’m missing something important.
In contrast to the UNIX shell, where you can see everything that comes out of a command, you can always try to make sense of it yourself, even if you’re not familiar with grep, sed, awk, jq or whatever tool makes this easy for you.
UNIX shell also allows me to intermediate results with the > operator, or just copy/pasting out of the terminal, edit the file with $EDITOR, and feed it to the next command with <. This is great UX and I suspect it would either not work, or have massive gotcha’s in PowerShell.
In contrast to the UNIX shell, where you can see everything that comes out of a command
That’s not always true. istty() and detecting terminal size can play tricks on what you get as the output.
This is great UX and I suspect it would either not work, or have massive gotcha’s in PowerShell.
It’s just $foo = ... - you can save the whole object for processing later.
Nothing stops you from processing the text output from the commands either. You just don’t normally have to do it. And it’s a good idea not to, because trying to reparse random strings will end in frustration one day.
I’m writing a piece defending PowerShell and plan to address this! Text isn’t as composable as objects are. For example, you can see the timestamps of all your files with ls --full-time but can’t extract them to another program, because all the other text is in the way (cut doesn’t work, I tried). You have to instead write stat -c %w.
In PowerShell, by contrast, you can just write ls | Select LastWriteTime.
I believe PowerShell is ONLY objects, not text. Because the objects are really data structures in a .NET virtual machine that are passed to other “cmdlets” ?
That sort of works on Windows, if the .NET VM has bindings to the entire operating system. It doesn’t really work on Unix.
It’s what I call a “two-tier” design of a shell … some programs are “privileged” cmdlets, while others are “external”.
I didn’t follow all of the winget argument (never used it), but I think the problem is that it’s external and not a tool that only lives in a .NET VM.
Even within a single Windows machine, you have native processes, .NET apps, and also WSL processes I believe. Maybe even WSL2. So it gets a bit messy.
Shell is for gluing together things that weren’t meant to be glued together.
PowerShell is probably a great tool in its domain, but I’d argue that it’s not fully general glue. This WinGet example seems like evidence of that – you have to “opt in” and “boil the ocean” to interoperate.
Most PowerShell guides I’ve seen focus on what you should type, not why; I learn better with the latter. For example, you use Select in your example, while another post used Get-Member for something I’d consider a similar operation. I can get a general gist what the difference is (one operates on a table, one operates on an object) but I find it difficult to train my duck-fu [1] to find the answer.
find . -maxdepth 1 -printf '%p\t%t\n' should do what you want, but I certainly agree that PowerShell is more readable here.
Get-Member lists out the properties of whatever type was piped into it, along with any methods or similar.
Select-Object allows you to select parts of the piped data:
-First n
-Last n
-Skip n
-Properties prop1, prop2 (This removes all properties except for the ones you list)
-ExpandProperty prop (Selects a property, and only lets the property through instead of the object itself)
Where-Object operates on the stream like a WHERE query in SQL.
Furthermore,
winget list
will truncate long names (and IDs?) to fit the terminal width or to some arbitrary width when piped to a non-tty (or whatever the Windows equivalent of that is), which makes its output really terrible to use in scripts.I guess this blog post illustrates how I still can’t get used to PowerShell. Author runs
winget upgrade
and, judging from the output, expects to be able to pipe this toSelectObject Id
which fails becausewinget upgrade
doesn’t produce PowerShell compatible output.It seems to me that PowerShell users, by seeing a table in the output, automatically assume that there’s some underlying data model, where each row in that table can be converted to (or is?) an object in some way.
That means that when you run a PowerShell command, it will have invisible output that becomes visible once you pipe it through the right command. Me not knowing this magical command, will therefore not have access to information that is in an object right in front of me. It constantly makes me wonder if I’m missing something important.
In contrast to the UNIX shell, where you can see everything that comes out of a command, you can always try to make sense of it yourself, even if you’re not familiar with
grep
,sed
,awk
,jq
or whatever tool makes this easy for you.UNIX shell also allows me to intermediate results with the
>
operator, or just copy/pasting out of the terminal, edit the file with$EDITOR
, and feed it to the next command with<
. This is great UX and I suspect it would either not work, or have massive gotcha’s in PowerShell.I think you’re used to one environment more than the other. Some counterexamples:
Use Get-Member on an unknown object to learn more https://learn.microsoft.com/en-us/powershell/scripting/samples/viewing-object-structure--get-member-?view=powershell-7.3
That’s not always true. istty() and detecting terminal size can play tricks on what you get as the output.
It’s just
$foo = ...
- you can save the whole object for processing later.Nothing stops you from processing the text output from the commands either. You just don’t normally have to do it. And it’s a good idea not to, because trying to reparse random strings will end in frustration one day.
I’m writing a piece defending PowerShell and plan to address this! Text isn’t as composable as objects are. For example, you can see the timestamps of all your files with
ls --full-time
but can’t extract them to another program, because all the other text is in the way (cut
doesn’t work, I tried). You have to instead writestat -c %w
.In PowerShell, by contrast, you can just write
ls | Select LastWriteTime
.I would note that the two options aren’t mutually exclusive. You can have text AND objects, by using a serialization format like JSON or TSV.
There are many projects in that vein:
https://github.com/kellyjonbrazil/jc ( I think it has a wrapper around ls to do what you want ?)
https://github.com/oilshell/oil/wiki/Structured-Data-Over-Pipes
I believe PowerShell is ONLY objects, not text. Because the objects are really data structures in a .NET virtual machine that are passed to other “cmdlets” ?
That sort of works on Windows, if the .NET VM has bindings to the entire operating system. It doesn’t really work on Unix.
It’s what I call a “two-tier” design of a shell … some programs are “privileged” cmdlets, while others are “external”.
I didn’t follow all of the winget argument (never used it), but I think the problem is that it’s external and not a tool that only lives in a .NET VM.
Even within a single Windows machine, you have native processes, .NET apps, and also WSL processes I believe. Maybe even WSL2. So it gets a bit messy.
Shell is for gluing together things that weren’t meant to be glued together.
PowerShell is probably a great tool in its domain, but I’d argue that it’s not fully general glue. This WinGet example seems like evidence of that – you have to “opt in” and “boil the ocean” to interoperate.
I’m looking forward to read it!
Most PowerShell guides I’ve seen focus on what you should type, not why; I learn better with the latter. For example, you use
Select
in your example, while another post usedGet-Member
for something I’d consider a similar operation. I can get a general gist what the difference is (one operates on a table, one operates on an object) but I find it difficult to train my duck-fu [1] to find the answer.find . -maxdepth 1 -printf '%p\t%t\n'
should do what you want, but I certainly agree that PowerShell is more readable here.[1] Adaptation of google-fu
Get-Member lists out the properties of whatever type was piped into it, along with any methods or similar.
Select-Object allows you to select parts of the piped data:
-First n
-Last n
-Skip n
-Properties prop1, prop2 (This removes all properties except for the ones you list)
-ExpandProperty prop (Selects a property, and only lets the property through instead of the object itself)
Where-Object operates on the stream like a WHERE query in SQL.
It is not, and has never been, and carries all the well-known problems of intermingling visual representation with data.
There is no more magic here than there is in the beautiful UX of aptly-named tools such as “awk”, “grep” and “sed”.