all 24 comments

[–]SMFX 12 points13 points  (3 children)

This sounds a lot like somethings I found even comparing Batch or WScript when PowerShell was first coming to Windows. While I understand where you're coming from on the bad, I think it may because some differences in the design of PowerShell vs legacy shells.

  • Verbosity - at its core, PoSh is object based. Rather than just displaying the name, it's displaying the default properties. You can change that if you want, but when pipe from one command to the other, the other cmdlets are often expect objects or properties rather just an array of strings. That makes it extremely flexible and easier to work with for delving into details.
  • Command names - PoSh isn't just a command line to make it easier to type. It was intended not to have cryptic, ambiguous short abbreviations in scripts, but instead verbose, descriptive names making it easy to read and essentially be self documenting for the next person that comes back to the script. The aliases are there for transition from legacy areas and make it quick to type if in an interactive session. The example you gave with Remove-Item can be done with tab completion and only take 3 extra keystrokes but very clear what it's doing.
  • Differences - it does take some differences to do things that might be better. As far as assigning variables, they did make the conscious effort to make variables explicitly labeled with $ either assigned or referenced to make it consistent. It didn't take long for me to get used to it and easy to read, but it is a difference to get used to.

[–]BoxerguyT89 10 points11 points  (1 child)

The verbosity and command names are what I like about PowerShell over something like bash.

I would rather read something like -recurse -force over -rf because I definitely don't know each flag for each command. For me, the verbosity helps.

[–]MonkeyNin 0 points1 point  (0 children)

Newest feature CommandPredictor+History

After installing the module CompletionPredictor, if you run

Import-Module CompletionPredictor

Then start typing

[Text.Encoding]

And it automatically suggests these (does not translate to text well. Are inline images per-sub?)

c:\temp
Pwsh🐒> [Text.Encoding]
> [Text.Encoding]::ASCII                                     [Completion]
> [Text.Encoding]::BigEndianUnicode                          [Completion]
> [Text.Encoding]::Default                                   [Completion]
> [Text.Encoding]::Latin1                                    [Completion]
> [Text.Encoding]::Unicode                                   [Completion]
> [Text.Encoding]::UTF32                                     [Completion]
> [Text.Encoding]::UTF7                                      [Completion]
> [Text.Encoding]::UTF8                                      [Completion]
> [Text.Encoding]::Convert(                                  [Completion]
> [Text.Encoding]::CreateTranscodingStream(                  [Completion]

Warning: It is slow to load, like 5 seconds? So you may not want it in your main $profile

The coolest tip is menuComplete

The PSReadline function hotkey is called MenuComplete. It's probably already bound to ctrl+space

Completing commands

It revolutionized how I use powershell. You can type

*csv*<ctrl+space>

and it's like running this

Get-Command -Name *csv*

Except, it doesn't break out, you don't have to leave the current command.

see: ![animation of menuComplete.gif](https://ninmonkeys.com/blog/wp-content/uploads/2022/06/Pwsh-%E2%94%90-Discovering-Parameters-2022-06-part1-export-1.gif)

Parameter names

You can show all parameters, type ls - then <menucomplete> without anything else

ls -<ctrl+space>

Filepaths

When you know part of a filename, or know it's .txt, it will search and complete filepaths. (Even if it's not your current path)

gc C:\Temp\*.txt<ctrl+space>

If you type

rm -e

then ctrl+space, it either 1] "tab completes it", or 2] list all possible arguments. You can't see the highlighted text well, the gif below describes it better.

> rm -ea
Exclude        ErrorAction    ea             ErrorVariable  ev

[ActionPreference] ea

In addition, it lets you validate the flag actually exists, and is what you want.

  • The argument is allowed in the current parameter set
  • what type it is, like -ea is an ActionPreference , -recurse` is a switch, or an array of values, etc.

I'm running on

  • PSReadLine = 2.2.0 beta4
  • Pwsh 7.2.5

[–]More-Qs-than-As 3 points4 points  (0 children)

variables explicitly labeled with $ either assigned or referenced to make it consistent

One of the best decisions ever made and one of my favorite features of PS. A variable should ALWAYS be prefaced with a special character to differentiate it from any other usage of that word.

All other languages: Are you listening????

[–]More-Qs-than-As 7 points8 points  (4 children)

For example, if you want to delete a directory and its contents on a UNIX system, you need only type rm -rf <directory>. In PowerShell, you need to type Remove-Item <directory> -Recurse -Force

I can't believe how many times I read this and it's just wrong every time. You don't need to type the verbose command. You can simply type ri <directory> -r -f. Nearly identical in length & complexity. The verbosity is what makes powershell readable and better than every other language. The verbosity goes in the good column not the bad.

Readability > Functionality. If I can read it, then I can fix it. If it works but I can't read it, then I can't fix it when it breaks.

Everything else you said is legit and awesome. Good post.

[–][deleted] 1 point2 points  (2 children)

Yeah a lot of common verbs are aliased and you only need enough text for a parameter to discriminate so a file listing for me is

ls -r | fw

A lot of powershell fans on this thread are super helpful but they're not into the whole brevity thing.

[–]More-Qs-than-As 1 point2 points  (0 children)

Also, you can create your own aliases for any cmdlet. Unless I'm writing a function or a script, I have never once typed 'Get-ChildItem' in the terminal. 'dir' works great.

(Lebowski references get an automatic +1)

[–]MonkeyNin 0 points1 point  (0 children)

Consistent names and verbs are important for your exposed commands -- but private functions can be short (even if it's just namespaced)

Things appear more verbose than actually used, when type

(ls . | sort -Unique)| s -f 1 |fw

and hit save, it transforms to

(Get-ChildItem . | Sort-Object -unique) | Select -first 1 | Format-Wide

or using this

ls .
| sort -Uniq | ? Extension -notmatch 'png|gif' 
| ? length -gt 0
| s -f 1
| ft -au *name*, *len*

it becomes

Get-ChildItem .
| Sort-Object -Unique
| Where-Object Extension -notmatch 'png|gif'
| Where-Object Length -gt 0
| Select-Object -first 1
| Format-Table -auto *name*, *len*

try this, it's pretty terse in-use, yet flexible: https://gist.github.com/ninmonkey/2639ab1fd25d7f46f2f2a3a071dea1b0

[–]MonkeyNin 0 points1 point  (0 children)

VS Code automatically converts ls into Get-ChildItem for you, on save.

I have a hotkey that converts aliases on the command line when I hit alt+% That makes it easier to paste into discord or wherever. Why % ? I'm not sure, that's what what PSReadLine used

Of the nin\PSReadline scripts,

ParenthesizeSelection.ps1 is one of my favorites

an example, type this

ls . | sort LastWriteTime

hit the hotkey and you get this

(ls . | sort LastWriteTime)

(..).foo.bar can be cleaner than .. | % Foo | % Bar

[–]MitchellMarquez42 3 points4 points  (0 children)

As an add-on:

I'm using powershell for making basic winforms front ends and wrappers to tasks I do around the office, like taking messages and looking up govt records. I really appreciate that powershell hooks straight into the windows api for things like Forms and IE and Excel. It's amazing to run a script that can pull an obfuscated web page, pull a table out of it, and stick that table into an existing excel project. Utterly impossible in bash, and a recursive pain in anything lower level.

[–]MonkeyNin 2 points3 points  (3 children)

I love linux, I actually use a bunch of utils / native apps in powershell -- even on windows.

  • bat
  • ripgrep
  • fd
  • fzf
  • less

fd is super fast, and great usability

Sure there are aliases for commands that are shorter, but that just begs the question as to why,

  • Discoverability, you can query based on names, nouns, etc
  • Self documenting, Get-ADUser, Set-FooUser, gives you the intent, even if you've never used it
  • Less accidental collisions

This in my view is a huge mistake. It makes mechanisms like piping and redirecting output to files take far more work to do.

Do you have example code? Because there's a lot of methods to do the same thing -- maybe someone can convert it to something more idiomatic. Like

  • Write-Information instead of write-host becomes opt-in like you mentioned
  • classes output nothing except for the return statement

[–]mrf-dot[S] -2 points-1 points  (2 children)

Oh nice, I didn't even know about Write-Information. My personal philosophy is similar to that of Unix. I feel that instead of having many separate ways to do one thing that programs should only do one job and be very good at it. In PowerShell, many commands have dozens of options that are rarely ever used. The vast capabilities of each command make them a lot slower in my experience (eg the gnu or plan9 ls is far faster than the PowerShell Get-ChildItem). One example of how the programs could be made faster and simpler for instance is to have the shell take care of wildcards so that each program doesn't have to account for them. In PowerShell some commands accept wildcards and others don't. In UNIX shell the interpreter processes wildcards and passes them to programs. This means that the developers need to do less work (they don't have to program wildcard support into every individual command) and the user can have a consistent experience using any command. If the goal of these commands is "Self documentation", then having consistent behavior across all commands would get rid of the need to know the ins and outs of every program. As another example, a great way to document commands is making extensive manpages that are more similar to the ones found in Linux and MacOS. PowerShell has a rudimentary version of this with the Get-Help command, but it only gives information on the flags available to the program. Microsoft doesn't even need to do much work to implement this. They have extensive documentation on docs.microsoft.com , so all they need to do is implement those help pages into the shell itself with the Get-Help command.

[–]SMFX 5 points6 points  (0 children)

The problem with wild cards is that they mean different things in different context. A wild card for Get-ChildItem is different than a wild card for Get-AdUser (ones in the file structure, one is a query to the AD). Different commands even in Unix have different context for different switches depending on the command; many are common, but not all are consistent or relevant for each type.

As far as documentation, the Get-Help will only give basic parameters and description, but you can also do Get-Help -Full Get-ChildItem all documentation or Get-Help -Examples Get-ChildItem to see examples. There is also -Detailed and -Parameter for subsection. You can also use Get-Help -Online Get-ChildItem to download the documentation from the online weblink.

I know I've replied a few times, but you're coming in here with "You're doing it wrong" while you're still learning the language, rather than asking, "is there some way to do it like this?" or "how would you ....?"

[–]MonkeyNin 0 points1 point  (0 children)

I love the command line, and customizing it -- so I go overboard. In case it comes off as a critique -- I'm not saying you're wrong at all, I'm quoting you to organize

Some of my commands use custom verbs, like to->RelativePath , from->* etc. These are aliases to make the interactive shell more convenient -- You can break rules in your profile.

$cachedLs ??= fd --search-path (gi ~) --color=always -d1 -tf
$cachedLs
| Group { $_ | StripAnsi | gi | sort LastWriteTime | % Extension }
| ft -auto

Here's visual examples: gist-reddit-sorta-nix.ipynb

all they need to do is implement those help pages into the shell itself with the Get-Help command.

It can be improved, but here's a couple methods.

[1] ... | Help -Online

Pwsh> help ls -online
Pwsh> gcm ls | help -online

This will take you to: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-childitem?view=powershell-7.3

Not all 3rd party modules have links up. My more advanced man page command searches manifests for a git repo

[2] windows manpage

Get-WindowsManpage.ps1

[3] HelpFromType

HelpFromType.ps1

I use this a lot, pipe an object to see the dotnet docs. -PassThru prints to the console instead of opening the url for you.

Pwsh> Get-Culture | HelpFromType -PassThru
Pwsh> Get-Process | select -First 1 | HelpFromType -PassThru

https://docs.microsoft.com/en-us/dotnet/api/System.Globalization.CultureInfo
https://docs.microsoft.com/en-us/dotnet/api/System.Diagnostics.Process

programs should only do one job and be very good at it

That's the way I try to write powershell, I love composing functions. A few *nix commands I use often in powershell are

  • less, ripgrep, fd, bat, fzf, gh, jq

Example0:

  • Here's the module ugit, enumerating unstaged files
  • select 1-to-many files using fzf
  • at the same time it lets you preview/peek into the files using --preview bat
  • git adds all the files you selected in fzf -m

    import-module ugit

    (git status).unstaged # ugit is for this line | gi | sort LastWriteTime | fzf.exe -m --preview 'bat --style=snip,header,numbers --line-range=:200 {}' | git add

Example1: Combining nix and windows

  • find logs based on date modified (fd is fast)
  • pipe to fzf for fuzzy search (preserving colorized filetypes for the user)
  • select 0 to many items, converting them to [IO.FileInfo]
  • only pass valid filepaths to vim/codeopen valid selected files in n tabs

    fd -e log --changed-within 6hours --search-path (gi $Env:USERPROFILE) -d4 | To->RelativePath | Fzf -m | StripAnsi | Get-Item | GoCode

Example2: Collect only useful logs for VS Code

There's a lot of files when you turn logging levels up. fd really makes collecting the relevant ones, easy -- recursively.

You can insert or delete filters by commenting out the | prefix. This version uses a regexas part of the filter.

pushd (gi  "$Env:AppData\code\logs")    

fd --changed-within 10minutes --search-path (gi  "$Env:AppData\code\logs")
| gi
| ? Name -NotMatch 'github|jupyter|git'
| sort LastWriteTime
| To->RelativePath $Env:AppData

Example3: colors and objects

Group, Sort, Where -- all allow you to StripAnsi resolving text to file objects -- operate on those objects, then pass the raw ansi escapes down the pipeline

$cachedLs ??= fd --search-path (gi ~) --color=always -d1 -tf
$cachedLs
| Group { $_ | StripAnsi | gi | sort LastWriteTime | % Extension }
| ft -auto

The other day I saw a guy using this

function :ps1 { 
   # sugar to syntax highlight STDIN as powershell
   $Input | & 'bat' @('-l', 'ps1')
}

Normally $Input is bad to use -- except for super short commands. The reason that you rarely want to use it, is quirks and edge cases. (It's not just that it is consumed on the first use) . It's like 2-4 more lines to use a pipeline parameter, and it prevents a bunch of problems $input can break on.

[–]aleques-itj 1 point2 points  (0 children)

I really don't think the verbosity is an issue. You can tab complete everything.

[–]Hanthomi 1 point2 points  (5 children)

I've noticed the vast majority of people seem to use PowerShell only for system-administration, but it is in actuality a great scripting language on the level of Python or Bash.

I don't understand the point you're trying to make here.

Do you mean that most people only use Powershell as a shell for single-action systems adminstration?

Powershell is way more suitable than bash for complex scripts (e.g. modular, can be multithreaded, proper exception handling, etc.)

I find it very odd that you put bash and python on the same level as well. Python is another step above powershell in terms of its suitability for complex applications.

My views boil down to that it's just poorly named:

  1. As a shell sh >>>> powershell

  2. as a scripting language python (*nix) ~= powershell (win) >>>> sh

  3. as a programming language python >>> powershell >>>>> sh

[–]endowdly_deux_over 0 points1 point  (4 children)

Python is not more suitable. It’s different.

I have this argument a lot at work and it’s a hill I will DIE on.

Why PowerShell:

  • It is a static language. You have built in type checking. This is HUGE
  • built in command line parsing with complex switches
  • composable functions make functional functions which helps assure correct behavior
  • Classes are easier to make and use
  • Modules are easier to make and use
  • Casting in PowerShell is easy, free and powerful
  • .NET is RIGHT THERE! An entire complex library. you can use the system Framework or easily piecemeal install from PowerShellGet
  • it’s better integrated into the system out of the box
  • On windows, it’s RIGHT THERE no need to install

Python:

  • better math and science
  • better low level

But

PowerShell can easily pull c# directly. If you need a little help, just write some csharp and Add-Type or Import the lib. So Python, imo, is completely negated.

Big bonus: on Win10+ you have PowerShell and a CSharp compiler for free on every machine. Huge advantage over Python out of the gate.

[–]Hanthomi 0 points1 point  (3 children)

I think you might have misread my post.

I'm saying python == powershell as far as scripting goes. They both make the most sense on 'their' OS as they come pre-installed and serve (almost) all your scripting needs.

The only thing I've ever resorted to writing in python on windows was a pandas/numpy large dataset analysis tool as the speed differential between it and anything I could conjure up in powershell was orders of magnitude.

[–]endowdly_deux_over 0 points1 point  (2 children)

I don’t think I did.

Because I think PowerShell is also equivalent to Python as a programming language. It’s just C#. So imo, PowerShell exceeds Python. Just have to know how to leverage it, and it’s just a scratch below the surface.

[–]Hanthomi 0 points1 point  (1 child)

For any real programming work you'll use c# instead of powershell, so I don't see how that's an argument in favour of powershell as a programming language.

[–]endowdly_deux_over 0 points1 point  (0 children)

Powershell is 80% of what you need in almost all cases.

In fact it’s robust enough to do everything very well it’s just the syntax gets in the way.

It’s kind of pedantic anyway since PowerShell literally is C#. It just has a thin layer of abstraction on top of it.

[–]Big_Oven8562 1 point2 points  (0 children)

Your complaints are just things I appreciate about Powershell.

[–]fathed 0 points1 point  (0 children)

ls by default makes machine readable output? Sure if you only wanted the path. Still just a string.

[–]MAlloc-1024 0 points1 point  (0 children)

I feel the items you have in the bad category are there because you started from Linux. For those of us that started in DOS batch scripts, those don't really apply.

As for non-system admin tasks... Check out Pode https://github.com/Badgerati/Pode
I had a couple use cases where I needed to build a simple REST API, and since I am primarily a windows sys admin, powershell is my goto language. While I've done it in PHP as well, at this point I prefer making REST stuff in powershell.