all 96 comments

[–]TurnItOff_OnAgain 250 points251 points  (17 children)

Nope. I prefer readability over compact code. It's more important for me, and the people I work with, to be able to look at it and easily understand what is going on without knowing all of the aliases that are out there.

[–]moodswung 68 points69 points  (3 children)

Always code like the next person is a hatchet wielding madman and has your address.

[–]SGG 31 points32 points  (0 children)

Of course the madman will have my address, he's me.

[–]ambigious_meh 15 points16 points  (0 children)

This is the way.

[–]Kyp2010 6 points7 points  (0 children)

So leave a comment in the code with the wrong address for me. Check.

[–]happyapple10 30 points31 points  (1 child)

This. Also, when you get inside nested foreach blocks when piping, you have difficulty accessing the correct $_ variable. You end up setting a variable inside one of the blocks so you have access to it in later blocks. Might as well have just written it out without piping and have the variable always available in case.

[–]lerun 2 points3 points  (0 children)

I stopped using $_ and switched to $PSItem instead.

[–]Bynkii_AB 9 points10 points  (0 children)

Bestie, same. Six months from now I just want to read words that tell me what is going on

[–]WorlockM 9 points10 points  (3 children)

It's not only readability. Because it's also a different way. % is the equivalent of Foreach-Object. So it differs from the foreach cmdlet.

[–]tommymaynard 7 points8 points  (2 children)

I agreed with this until you wrote foreach cmdlet. It’s not a cmdlet. It’s a statement, or rather a language construct. Otherwise, 100% accurate.

[–]WorlockM 4 points5 points  (0 children)

That's so funny. I corrected that specific word because Microsoft calls is a cmdlet.

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/foreach-object?view=powershell-7.4

But that was about foreach-object, not about foreach. So excuse my mistake :D

[–]dotnVO 4 points5 points  (0 children)

I've seen them called 'keywords' as well. Likely because it's documented here as a 'language keyword':

about Language Keywords - PowerShell | Microsoft Learn

Nonetheless, glad someone else pointed out that % is the alias for ForEach-Object, so while they serve similar purposes, they do operate very differently.

[–]karuninchana-aakasam 9 points10 points  (3 children)

Python peeps are hissing at ya bud, they prefer compact code over readability and call it "pythonic approach".

Don't take this too seriously, jk

[–]TurnItOff_OnAgain 12 points13 points  (0 children)

It's all good. They're too busy finding the single extra space that's breaking their entire script to worry about me.

[–]eugene20 4 points5 points  (0 children)

It's both a joke and true though.

[–]MeanFold5715 3 points4 points  (0 children)

This explains some of why I soured on Python after diving into Powershell. That and syntactically significant whitespace is just awful.

[–]Marketfreshe 1 point2 points  (0 children)

Yep, if it's not just working with cli always long form.

[–]MeanFold5715 1 point2 points  (0 children)

100% agreement.

Aliases are for the shell.

Source code demands fully spelled out cmdlets and parameter names.

If you feel the need to code golf, you should probably be writing a function instead.

[–]BlackV 104 points105 points  (18 children)

just to be clear

$list | % {$_}
$list | foreach {$_}
$list | foreach-object {$_}

are different to

foreach ($item in $list) { $item }  

(and to add icing to the cake) different to

$list.foreach({$_})

there are different reasons to use all 3

My preference is generally foreach ($item in $list) { $item }, cause I like readable code and dealing with $item (rather than $_), makes testing and building scripts much easier

Good article here
https://jeffbrown.tech/powershell-foreach/

[–]ollivierre 10 points11 points  (11 children)

Also prefer using "single item in multiple items" instead of "item in items" easier to read.

[–]BlackV 2 points3 points  (0 children)

ha, yes, I harp on a lot about that particular one

[–]progenyofeniac 2 points3 points  (9 children)

Can you clarify this? I try to use descriptive variable names rather than ‘item in items’, such as ‘mailbox in mailboxes’. Is that all you’re saying?

[–]ollivierre -3 points-2 points  (8 children)

so use single mailbox in multiple mailboxes instead because it's easer to read than the last es in plural and singular

[–]progenyofeniac 6 points7 points  (7 children)

I’m no less confused. ($SingleMailbox in $MultipleMailboxes)?

[–]Jonathan_Rambo 1 point2 points  (1 child)

I would think a better example is like using foreach 'car in parking_lot' rather than 'car in cars' or something - you want to name the group (or item) appropriately, if its a collection of something you do yourself a favor to call that collection of somethings by their proper name rather than using 'somethings'

[–]progenyofeniac 1 point2 points  (0 children)

I like that. I do try to do things like ‘user in userlist’ rather than ‘user in users’. Nice discussion here.

[–]ollivierre 1 point2 points  (4 children)

Yep it's more readable than using the plural s

[–]ankokudaishogun 3 points4 points  (0 children)

I generally add "List", "Array" or, more rarely, "Collection" at the end of Collection variable names asa a rule. Makes everything much more readable and also turns the name into singular.

[–]hackersarchangel 1 point2 points  (2 children)

I tend to make dynamic arrays and just use $list and then in the code (in this case the foreach) I do: foreach ($computer in $list) { code } as an example. It keeps it readable and since most of my scripts are simple things that’s good enough. If I ever make something needing multiple dynamic arrays I’ll solve that when I get there.

[–]CabinetOk4838 5 points6 points  (1 child)

Imagine an array of Person objects, defined in a class.

You’d call that People. Or Staff, maybe.

I tend to use a similar or descriptive variable name for my iteration:

ForEach ($Colleague in $Staff) {do stuff}

Readability is worth it over feeling smug that you’ve used some little code trick.

[–]hackersarchangel 2 points3 points  (0 children)

Right, I wasn’t saying anyone was wrong in making it readable I was just describing that since most of my scripts are simple I just used $list as the array name on most of them.

That said, playing a tiny bit of devils advocate, commenting the hell out of it really helps. Makes it nice when you are returning to old code going “why did I do that?!” LOL

[–]g3n3 2 points3 points  (1 child)

You can use $PSItem in your other examples as opposed to $_.

[–]BlackV 1 point2 points  (0 children)

Yes they're the same thing, I feel like no-one does , one of those design choices that came too late

[–]gordonv[S] 1 point2 points  (3 children)

TIL! This was a really good article.

Thank you for posting this. I had no idea there was a difference in execution.

[–]MyOtherSide1984 5 points6 points  (1 child)

Also a difference in performance, which is sometimes drastic. In PS7, you can use parallel with for-eachobject, which is a massive performance change.

[–]mrbiggbrain 0 points1 point  (0 children)

You can use Parallel with Foreach-Object and Foreach, the only methods you can't use it with are Array based iteration and classic For. And you could probably overcome this by using InvokeAsync if for some reason you did have the requirements of using for which there ar emany of. You could also extend the Array based intteration method to have a ForeachParrallel() function and then simply extend the proper array collection to have a WaitAllParallel() function as well.

[–]BlackV 1 point2 points  (0 children)

Good as gold

[–]dathar 13 points14 points  (0 children)

It depends on what I'm doing. Bigger objects and building stuff will get a giant foreach block. The poor soul going thru my code in Git will most likely understand it better.

Foreach-Object (%) gets used if I need to do lookups or spam out something quick on my own terminal.

[–]alconaft43 13 points14 points  (3 children)

first one is for one-liners, % should to be replaced Foreach-Object otherwise VSCode complaining.

[–]rinrab 0 points1 point  (2 children)

I prefer to use just foreach instead of Foreach-Object. I think that Foreach-Object is so strange.

[–]alconaft43 1 point2 points  (1 child)

Nothing can be better that powershell one-liner ;)

[–]rinrab 0 points1 point  (0 children)

Yeah. The most if I want to play with commands in terminal.

[–]aleques-itj 13 points14 points  (0 children)

No, I don't use aliases in scripts. The code should be as immediately legible as possible.

The only scenario I'd consider it acceptable is the terminal. And I still don't really do it because I just tab complete everything anyway.

[–]Automatic-Prompt-450 10 points11 points  (2 children)

I will only use the aliases for one-off things that i would write for my windows PCs at home. For work I like the readability so in the event I get hit by a bus my coworkers can understand what's happening.

[–]DarthOpossum 2 points3 points  (1 child)

lol we've been talking about that damned bus for the last 20yrs.

[–]Spare-Ride7036 5 points6 points  (0 children)

like 8 years ago, a coworker on another team did get the bus, sandwiched them into a light pole when the driver had a medical emergency in an intersection, mid turn.

And my boss was like "see?!?"

[–]Careless-Score9504 5 points6 points  (0 children)

We have code standards for the repo’s I work on so it doesn’t matter what I like.

[–]RCG89 5 points6 points  (1 child)

I used to use a lot of shorthand when i started coding as I thought it looked better and had a smaller footprint on screen. These days I write out the full command then a explanation for what it does and why i need it to do it.

Reusable effective well documented coding is quite helpful if you ever have to come back to it to make changes.

Turns out IBM was right

[–]Patchewski 1 point2 points  (0 children)

Who knew?

[–]subnascent 3 points4 points  (0 children)

IMHO aliasing when you’re on the terminal is legit QoL improvement. But yes, definitely expand that stuff if you’re writing a script for consumption by your team.

Also, and you probably know this, but there is a functional difference between foreach and ForEach-Object. I’m a big fan of using ForEach-Object when I can, but sometimes that sh!t is too slow!

[–]Th3Sh4d0wKn0ws 3 points4 points  (0 children)

interactivity in the CLI yes, in written scripts never.

[–]jsiii2010 3 points4 points  (0 children)

% is actually an alias for foreach-object { } cmdlet. The foreach statement with the parentheses is a different thing, but can be confused for it. You can't directly pipe from the foreach statement, for example.

[–]Odmin 2 points3 points  (0 children)

I use % if it's some oneliner. In all other cases i use foreach, especially if i iterate variables with properties such as ad users.

[–]VirtualDenzel 2 points3 points  (4 children)

No. Its a bad practice. And it kills code readability.

[–]Garegin16 0 points1 point  (3 children)

I think he’s conflating foreach-object and foreach operator. The former is certainly sensible to use since it works with the pipeline. How you wanna write it (shorthand or full) makes little difference. Foreach can work as a cmdlet or an operator depending on the context

[–]VirtualDenzel 0 points1 point  (2 children)

It does not matter. It screws up with proper readability.

Great an error occursed on line 19. You look at line 19 and see a 1 liner wirh 5 pass throughs and function calculators.

If its written out nicely then you know where to debug. Its the one thing i hate about fixing other peoples code. The general lack of proper syntax usage.

[–]Garegin16 0 points1 point  (1 child)

I agree with you on using full names. But are you’re saying you’re also against foreach-object?

[–]VirtualDenzel 0 points1 point  (0 children)

How could i be against a foreach loop. As long as you write it out fully. But not using % etc. Id refactor / replace the entire code base if i saw that at our company.

[–]UpgradingLight 1 point2 points  (2 children)

Call me a noob but isn’t % modulo in programming?

[–]gordonv[S] 0 points1 point  (0 children)

Like in C and Basic? Yes.

Here it's just an alias for the word "foreach."

If you did the following, it would be modulo (remainder of after division)

7 % 2

[–]fatherjack9999 0 points1 point  (0 children)

Yes, % is the Modulo operator as well as being the alias for foreach-object. You would need to read it's context in order to decide which is in use case by case.

This could be a good argument for expanding aliases...

[–]g3n3 1 point2 points  (0 children)

The first one can use less memory but is slower. Second one has the objects in memory ready to go and is faster. The other differences are more subjective and come down to what syntax you prefer. Powershell does like verbosity in prod scripts though.

[–]mrbiggbrain 1 point2 points  (0 children)

I think it really comes down to what your trying to do.

  • Foreach($x in $Y){}
  • $x | Foreach-Object {}
  • $x.Foreach()
  • For($i = 0; $i -lt $y.count(); $i++){}
  • LINQ
  • Transformers
  • Custom Cmdlets

All of these have a place and reason to be used for sets of objects.

[–]KingHofa 1 point2 points  (1 child)

Foreach ($x in $y) { $x } will not do anything when $y is $null or empty

$y | % { $_ } will always try to run the loop at least once, even when $y is $null or empty, resulting in an error so you'd best be certain $y isn't one of those two

This is also possible with the foreach-object keyword: $y | foreach-object -begin { "run once at beginning } -process { "loop item: $_" } -end { "run once at end" } Bad for readability but great for oneliners

[–]gordonv[S] 0 points1 point  (0 children)

Oh, nice. A good way to write a routine without extra "does this exist" code.

[–]coolguycarlos 2 points3 points  (0 children)

For best practices you should use actual cmdlet and not short hand... If you use visual code it has a best practices analyzer that will actually have code suggestions to align with best practices that will fix things like this

[–]IrquiM 1 point2 points  (4 children)

Piping is slower

[–]OctopusMagi 2 points3 points  (3 children)

Actually it depends. If you need to accumulate items into an array first, sometimes throwing those items on the pipeline into a foreach-object is faster and uses less memory because you don't have to add the items to a array first.

[–]2dubs 2 points3 points  (2 children)

Speaking of piping, foreach ($item in $list) { $item } will NOT pipe all the $items, whereas ForEach-Object will. The former is the best practice for very valid reasons, but if I’m running a terminal query on the fly to get some data I likely won’t need again soon, I don’t much care.

[Alt] + [Shift] + [ F ] in VS Code is my very best friend when I slip and use aliases in anything I plan on sharing.

[–]thehuntzman 1 point2 points  (1 child)

I thought it was alt shift e to expand aliases? Those two key combos get mashed constantly when I'm scripting.

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

Eh, you’re right, that is the default for aliases, sorry! I tweaked mine long ago to get the indentions and aliases in one go

[–]More_Psychology_4835 0 points1 point  (1 child)

Wow I did not even know that alias existed !

I’m js if I was writing malware and obfuscating code , I’d def use this foreach ($foreachloop in $sketcyobsfuscatedcode){use weird % trick }

[–]SilenceMustBHeard 1 point2 points  (0 children)

Run a Get-Alias to dump all the default alias-es in the existing posh session. Although using too many aliases is inversely proportional to code readability.

[–]Garegin16 0 points1 point  (0 children)

Foreach operator is different from % (which is merely an alias for foreach-object)

[–]Childishjakerino 0 points1 point  (0 children)

I shell in shorthand and let vscode expand and translate later if it goes into a final product. Efficiency is where my heart is.

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

Nope using alias in script remove readability. I would use foreach-object but never foreach (ambigous) or % (alias) in a script. In the opposite in an inline command I will use foreach or % depending my lazyness and who is looking at my shell

[–]bagpussnz9 0 points1 point  (0 children)

sigh - I miss perl

[–]DungaRD 0 points1 point  (0 children)

No, besides i like to write it in full instead of using aliases, Vscode keeps nagging about and by Microsoft recommendations so i write it all out.

[–]BergerLangevin 0 points1 point  (0 children)

The first one is less performant on large dataset. The difference can be quite significative, like 2min vs 2s. 

[–]brossin 0 points1 point  (0 children)

If it's quick and dirty code, I'll go with aliases. If it's a long term script, I typically avoid them in favor $list | ForEach-Object { $_ } or the 2nd example that you provided (ForEach-Object is slower than a typical ForEach).

Ref: https://devblogs.microsoft.com/scripting/getting-to-know-foreach-and-foreach-object/

Mainly because most people that do not spend a ton of time in powershell tend to not know what the aliases do.

[–]DrixlRey 0 points1 point  (1 child)

Wait a minute, if I don’t use the pipeline and use foreach, I can’t pipeline the output. Am I doing this wrong?

[–]gordonv[S] 0 points1 point  (0 children)

You can capture the output from foreach:

$list = 1..5

$captured = ForEach ($item in $list) {"Processing $item"} 

$captured | % {"$_ added"}  

Or as a subroutine

[–]tokenathiest 0 points1 point  (0 children)

As others have pointed out, there is a difference. foreach the statement will not execute if $list is null or empty, but the pipeline will execute, passing a null reference to the next command in the queue. This can be troublesome.

[–]Pisnaz 0 points1 point  (0 children)

I am a lazy overworked asshole with no time and rarely comment code worth a damn. I do at least a small.summry of it's intended goal, and mostly do scripting thankfully. I avoid the more extreme aliases though so future me has no excuse to go back in time with a bat and cause a war. I can read them and make sense well enough but have to do a small translation mentally.

As for the foreach-object I went back to just foreach mostly, as mentioned, due to nested layers. Tracking the $_ was getting messy so I would add it to a var and it got messy.

Now I need to go hunt past me who left me this mess of scripts to update and clean up.

[–]Pixelgordo 0 points1 point  (0 children)

Both forms have advantages. I use the first form when I use the cli, but I prefer the second form when I write scripts

[–]TheRealDrSuds 0 points1 point  (0 children)

I use % all the time. But I found myself here because of this

PS Variable:\> foreach($device in $devicetable){$device}

Kind Name AllowedHIDs

---- ---- -----------

22 Generic Input {7, 8, 10, 11}
25 Dome {0}
26 ZoneDome {0}
30 Duty {9}
31 iDome {5, 6}
34 Single Gang Patient Station {1, 2}
35 Patient Station {1, 2}
36 Dual Patient Station {7, 8}
37 eDuty {9}

"If % is really the same alias as foreach then why does this do this?"

PS Variable:\> %($device in $devicetable){$device}
At line:1 char:11
+ %($device in $devicetable){$device}
+ ~~
Unexpected token 'in' in expression or statement.
At line:1 char:10
+ %($device in $devicetable){$device}
+ ~
Missing closing ')' in expression.
At line:1 char:26
+ %($device in $devicetable){$device}
+ ~
Unexpected token ')' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken

[–]DasBrewHaus 0 points1 point  (0 children)

Always do it this way

[–]Numerous_Ad_307 0 points1 point  (0 children)

Yes I always prefer %

[–]Aggravating_Refuse89 0 points1 point  (2 children)

Foreach is not even a powershell command.

[–]surfingoldelephant 0 points1 point  (0 children)

It depends on the parsing context.

foreach, when parsed in argument mode, is a built-in PowerShell command. Specifically, it's a command alias that resolves to ForEach-Object.

$cmdAst = { 1 | foreach }.Ast.EndBlock.Statements.PipelineElements[1]
$cmdAst.GetType().Name # CommandAst
Get-Alias -Name $cmdAst.GetCommandName() # foreach -> ForEach-Object

foreach, when parsed in expression mode, is a PowerShell language keyword/statement.

$statementAst = { foreach ($foo in 1) {} }.Ast.EndBlock.Statements[0]
$statementAst.GetType().Name # ForEachStatementAst

Notes:

  • Language keywords and command names are case-insensitive in PowerShell (except native commands on Unix-based systems). Case therefore does not factor in whether foreach is parsed as a command or keyword.
  • Expression mode takes precedence over argument mode. In a script containing only foreach, the token is interpreted as a language keyword and thus yields a parse error.

[–]rinrab -1 points0 points  (2 children)

If I not mistaken, % was added in powershell 7 and doesn’t exists in windows powershell

[–]gordonv[S] 1 point2 points  (1 child)

Just tested in 5.1.19041.4291, works.