# Tips for golfing in PowerShell

What general tips do you have for golfing in Windows PowerShell? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to PowerShell (e.g. "remove comments" is not an answer). Please post one tip per answer.

—taken nearly verbatim from marcog's question.

• When I googled "PowerShell golf" this was the first hit!
– Matt
Mar 14, 2016 at 15:53

Powers of 10 literals with scientific notation:

4e6 = 4000000


Powers of 2 literals:

4KB = 4096
4MB = 4194304
4GB = 4294967296

# TB and PB suffixes also exist, but less useful for golf.


Could come in handy.

If you need to run a loop, and you know exactly how many times it needs to run every time, consider piping an array of contiguous integers into ForEach-Object via the % alias instead of using for.

for($x=1;$x-le10;$x++){...} vs 1..10|%{...} You can skip spaces a lot in PowerShell. If it feels like it might not be needed, it quite possibly isn't. This is particularly useful in comparisons. Example: $x-eq$i-and$x-ne9


vs.

$x -eq$i -and $x -ne 9  If you need to branch your script based on the result of a single test which may have multiple outcomes, switch can sometimes match or beat an if statement. Example (switch vs. if/else - tie): if($x%2-eq0){'even'}else{'odd'}


vs.

switch($x%2){0{'even'}1{'odd'}}  Or (switch vs. if/elseif/else - switch wins by 15): if($x%2-eq0){'even'}elseif($x%2-eq1){'odd'}else{'error'}  vs. switch($x%2){0{'even'}1{'odd'}2{'error'}}


If the switch is actually based on certain math results, like the modulo operation above, you can replace the switch entirely with an array. Here, it saves another 13 characters and is even shorter than the original two-option if/else statement. (Thanks to Danko Durbic for this bit.)

('even','odd','error')[$x%2]  If you will be using a particular command a lot, especially one without a pre-existing short-hand alias, set up a single-character alias early on. Example: nal g Get-Random;g 10;g 10;g 10  vs. Get-Random 10;Get-Random 10;Get-Random 10  • ('even','odd')[$x%2] FYI. Nov 16, 2018 at 2:09

Encapsulating the command that defines a variable in parenthesis allows you to feed the variable's definition directly to other commands.

For example, you can set $x and then set$y based on the value of $x in one shot with this: $y=($x=1)+1  Instead of this: $x=1;$y=$x+1


You can set $h and output it with this: ($h='Hello World!')


$h='Hello World!';$h

• This is especially useful for calling methods on objects. ($x=New-Object Windows.Forms.Form).Controls.Add($t) Nov 26, 2013 at 21:49

A switch can act like a loop, when given an array. For example:

$FooBarMeh='a','b','c' switch ($FooBarMeh)
{
'a'{'FOO'}
'b'{'BAR'}
default{'MEH'}
}


Will output:

FOO
BAR
MEH

I'm not totally sure where this will be useful, but I expect it will be handy for someone some time.

• It is handy every time you need a ForEach-Object and a switch within that. It's for example (in real-world) code very nice for writing quick parsers of text files where you need to do different things depending on which regex a line matches.
– Joey
Nov 24, 2013 at 12:02
• This is... damn weird. An array shouldn't match with a char, but it does.
– cat
Apr 24, 2016 at 14:56
• @Joey That's what switch -File $path is for Nov 16, 2018 at 2:10 • Not everything that's parsed happens to be a file. – Joey Nov 17, 2018 at 15:17 Want to find the maximum or minimum of a collection of values? Tried (...|measure -ma).Maximum  or (...|measure -mi).Minimum  already? Just sort and use the last or first item: (...|sort)[-1] # maximum (...|sort)[0] # minimum  • And, if you know the length of the collection of values and it's less than 10... Jan 10, 2016 at 14:12 • @wizzwizz4: Oh, maximum lengths can often be employed creatively, not necessarily being 10. Although for this particular case I don't recall an instance where it ever helped. – Joey Jan 10, 2016 at 14:53 • I mean, if the final item is known to be 0, 1, 2, 3, 4, 5, 6, 7, 8 or 9, it would save a byte to write the known length instead of -1. Jan 10, 2016 at 14:55 Replace [math]::pow with multiplication. Instead of [math]::pow($a,$b)  you can write "$a*"*$b+1|iex  This works for integer exponents >= 0. • I needed to see this answer to understand iex ... Alias for Invoke-Expression Jan 17, 2019 at 16:57 # Shortening Property Names Sadly, unlike parameters, properties/methods (anything accessed with a dot .) cannot usually be shortened down to its unambiguous form. But certain cmdlets can operate on property names and take wildcards, and there are little-known parameter sets of % and ? that can be useful. Usually we pass in a scriptblock and refer to the item with $_, but there's another form of these that takes a property name, and it accepts a wildcard.

$o|select Le*$o|%{$_.Length}  With a property like .Length we can't use the v3 magic that would normally work on an array because Length is a property of the array itself, so the above two could be used to get the lengths of the individual members. The select comes in a little bit shorter. But % can take a property name directly and return that value: $a|% Length


Which can be shortened with wildcards. The wildcard must resolve to a single property (or method, more on that later), so it will throw a helpful error if it doesn't, indicating exactly which members it could resolve to.

In the case of Length, Le* is typically the shortest. Even on a single string, this method is 1 byte shorter than just using the property.

$a.Length # 9 #(doesn't work on array)$a|%{$_.Length} # 15$a|% Le*                 # 8


But depending on what you're doing with this, this can be worse. You can do $a.Length*5 but to do it with the pipeline expression you'd have to wrap it ($a|% Le*)*5; might still be worth it if it's against an array, but the point is it's not always appropriate as a straight substitution.

It works with methods too, and you can leave off the () which makes a full name the same length, but same restriction as above about sometimes having to wrap it. The method must have an overload that takes no parameters (you can pass arguments by placing them after the method name, which is really nice):

$a.ToUpper() # 12$a|% *per                #  9


With arguments:

'gaga'-replace'g','r'    # 21
'gaga'|% *ce g r         # 16


These aren't strictly the same in that the -replace operator does a regex replace, but if you're just doing a string replace, it can (now) be shorter to use the method; it helps that the strings are cmdlet arguments instead of method arguments so they don't need to be quoted.

## Where-Object Properties

? can take (partial) property names as well, and apply an "operator" to it (in the form of switch parameters). Again this can be shorter than using the standard Where-Object scriptblock approach if the property name is sufficiently long and unique.

$a|?{$_.Length-gt5}      # 19
$a|? Le* -GT 5 # 14 ($a|% Le*)-gt5           # 14 - Lengths, not objs

• One of the few places where whitespace is required. Nice find, though. I can think of several areas where this will shorten things. Feb 27, 2017 at 15:48
• Nice edit Joey! Thanks. Feb 27, 2017 at 15:56
• Found you a way to get it shorter ;-) ... the sad thing about this is that as a whole it's a pipeline again, which limits its usefulness a bit. Or rather, there are a lot of places where you'd have to wrap it into parentheses again to get an expression. But few golf techniques are without trade-offs ...
– Joey
Feb 27, 2017 at 15:56
• @ConnorLSW ah yes I meant to update this as I'd since realized you can pass args that way, which greatly ups its usefulness. Great use of .ToString()! Apr 3, 2017 at 21:29
• @briantist this has turned PowerShell into a much more competitive language, really cuts out some of the annoyingly verbose .Net calls. Apr 4, 2017 at 8:19

Comparison operators work on collections of values by returning matching values:

1..5 -gt 2


will yield 3, 4 and 5. In some cases this can help to save an otherwise longer |?{$_...}. -match is a comparison operator too. • Note that you don't need spaces in that example 1..5-gt2 – Matt Mar 14, 2016 at 15:34 • I know; this was more about showing the technique. Spaces are in a separate answer. – Joey Mar 14, 2016 at 18:08 Use aliases whenever possible. There are a bunch of useful ones: ? Where-Object % ForEach-Object gu Get-Unique sort Sort-Object iex Invoke-Expression  for loops can have anything between 0 and three statements in their header: Endless loop: for(){}  Loop with initialization: for($n=0){}


Loop with initialization and end condition:

for($n=0;$n-lt7){}


In such cases the additional semicolons at the end may be omitted (it's explicitly stated in the language specification, so it's not an implementation detail) in contrast to C-like languages which always require exactly three statements.

This also makes while a bit shorter. Compare

while(...){}


and

for(;...){}


With the added bonus that you can stick in a previous line (if there is one) into the for as well without extra cost (and even saving a character).

Finding a sum the long way:

(...|measure -s).Sum


A shorter way:

...|%{$s+=$_};$s  And even shorter: ...-join'+'|iex  Semicolons and line breaks are interchangeable. Golfed code is often more readable if not jammed into a single line. And the length is still the same (provided you use U+000A as line break which PowerShell handles without problems). • Wait, we are worried about readability?! Nov 22, 2013 at 15:50 • If it has no effect on the length ... why not? – Joey Nov 22, 2013 at 17:02 • Doesn't it technically make a difference since a semicolon is one less character than \r\n? Only in Unix it is a singular \n. Jan 9, 2014 at 4:47 • @Vasili: You can save the files just fine with only U+000A between the lines. PowerShell won't complain. Which I wrote in the answer already, by the way. Line breaks are a property of the file, not of the operating system it is used on. No one says that you cannot use Unix line endings on Windows. – Joey Jan 9, 2014 at 5:50 • @Joey If you’ve used a fresh install of windows to do some quick programming, you’ll notice that Notepad doesn’t play nice with \x0a Dec 18, 2018 at 20:58 The Get verb is implied. This can shorten any Get-Frob to just Frob. Frequent contenders are date or random. Note that this won't work properly in some cases because you might have GNU utilities in your path (or other native programs that clash). Order of command lookup in that case seems to prefer the native program before it considers cmdlets with the Get- removed: PS Home:\> date Freitag, 15. November 2013 07:13:45 PS Home:\>$Env:Path += ';D:\Users\Joey\Apps\GnuWin32\bin'
PS Home:\> date
Fr Nov 15 07:14:13 W. Europe Standard Time 2013

• This doesn't quite always work out as expected. I have a script that starts with nal g Get-Random to save characters later on. Changing it to nal g Random causes the script to hang indefinitely (or, at least, take an inordinate amount of time to process - I haven't had the patience to wait for it to end, but it's taking several orders of magnitude longer than the original form before I abort).
– Iszi
Nov 15, 2013 at 0:56
• Two short Measure-Command invocations (100 times Get-Random vs. random) tell me it's about 500 times slower. I didn't know that before, to be honest. But it's good to keep it in mind, especially in loops with many iterations. That being said, golfed code should be short, not fast (that being said, it sucks to have to wait two days for an answer to a Project Euler problem).
– Joey
Nov 15, 2013 at 6:18
• My problem was running 10,000 iterations of the Monty Hall scenario each requiring three iterations of Get-Random. A 50,000% increase in processing time, multiplied across 30,000 runs is pretty nasty.
– Iszi
Nov 15, 2013 at 6:23
• Wow. It looks like the same probably holds true for any alias. Tested Get-Variable vs. gv and got a similar comparison.
– Iszi
Nov 15, 2013 at 14:43
• That could be it. I did some math and figured my Monty Hall script should take about 1.5 hours (normally, 10-11 seconds) to run without Get-. That's a pretty gnarly bloat in run time for a savings of just 4 characters in length.
– Iszi
Nov 15, 2013 at 14:49

If you are assigning an array that you know will only have two values, don't use indexing.

Something like this:

$a="apple","orange"$a[0] # apple
$a[1] # orange  Can easily be turned into this: $a,$o="apple","orange"$a # apple
$o # orange  This can also be useful for if you just need to the first element of an array: $a,$b=1..10$a # 1
$b # 2..10  • In this case (with the strings) $a="apple";$o="orange" is identical in length. It's the longer arrays that can sometimes be optimised fairly nicely, e.g. by putting all elements in a string with a separator and then using -split (best to use whitespace as separator because then the unary -split will suffice). – Joey Jun 2, 2014 at 6:03 Casting to string: [string]$x


vs.

"$x"  Casting to string like this can also be used to flatten an array of strings, instead of joining it: $a = @('a','b','c')
$a -join ' '  vs. $a = @('a','b','c')
"$a"  Casting a string to a numeric type: [int]$x     [float]$x  vs. +$x


Also very useful to know that PowerShell always takes the type of the left operand to determine the final type of an expression and conversions to apply:

'1'+2    -> '12'
1+'2'    -> 3


which can help determining where needless casts are.

Don't forget that you don't always need to provide the full name of a parameter, and some parameters are positional.

Get-Random -InputObject (0..10)


...can be trimmed to...

Get-Random -I (0..10)


...because "I", in this case, is enough to uniquely identify InputObject from the other valid parameters for this command.

You could trim it further to...

Get-Random (0..10)


...because InputObject is a positional parameter.

Piping is usually shorter than feeding objects as a parameter, especially when it can remove the need for parenthesis. Let's trim our random number generator further...

0..10|Get-Random


Also be on the lookout for other ways to accomplish the same thing, even if you can't change the command. For the above case, you could do this:

Get-Random 11


Or, incorporating another suggestion*:

Random 11


**Note: Omitting Get- from a command name can bloat the run time by about 50,000%. Not bad if you only need the command once, but be careful using it in long loops.*

And that's how can knock a simple command down to a third of its size.

Automatic variables have booleans for True and False as $true and $false but you can get similar results using the logical not operator ! and the integers 0 and 1( or any non-zero integer.)

PS C:\Users\matt> !1
False

PS C:\Users\matt> !0
True


Near all PowerShell expressions can be evaluated as booleans. So as long as you are aware of how certain data is evaluated you can get booleans and never need to explicitly cast them. Be aware of the LHS value when doing these.

• Integer 0 is false and non-zero integers are evaluated to true.
• non-zero length strings are true and empty or null (and nulls themselves) strings are false.

There are other examples but you can easily test by doing a cast

PS C:\Users\matt> [bool]@(0)
False

• Many other data types also have similar status. Uninitialized variables default to $null, which is falsey. The empty string (and variables set to the empty string) are falsey. Etc. This can then be used to shortcut indexing into an array (e.g., when doing an if/else), as was used in FizzBuzz. Mar 14, 2016 at 16:45 • @TimmyD Very true. Using ints is just shorter – Matt Mar 14, 2016 at 16:53 • @TimmyD I wanted to answer fizzbuzz until I saw yours.... Can't beat that .... at least not yet – Matt Mar 14, 2016 at 16:55 • Note that in many cases you don't actually need a bool. You can use 0 and 1 just as well. Implicit conversions help a lot in that regard (which you can often force with certain operators). – Joey Mar 14, 2016 at 18:09 When using a number as an argument to an operator that would otherwise require a string, you can use the number directly. Compare ...-join'0'  vs. ...-join0  Works with -split as well. The argument is always converted to a string first. • For reference, this works with -replace as well. Oct 24, 2017 at 13:23 • -replace also works with no second argument wheb you want to remove matches, – Joey Oct 25, 2017 at 9:05 Fake ternary operator. You can assign straight from an if statement: $z=if($x-eq$y){"truth"}else{"false"}


But you can use a 2-element array and use the test to index into it. $falsey results get element 0,$truthy results take element 1:

$z=("false","true")[$x-eq$y]  NB. that this is really doing array indexing, and if the test results in a value which can be cast to an integer, you'll ask for an item outside the bounds of the array and get$null back, and will need to do !(test) to force cast the result to a bool, with the options reversed.

[ Edit: There's a genuine ternary operator in PowerShell 7+ in the C-language-style cond ? 1 : 0 ]

• The first snippet didn't work at one point in time (older PowerShell versions), if I remember correctly, making it necessary to wrap it into $(). There was essentially a distinction of using pipelines made from commands and statements as expressions. Seems that is gone by now, at least in PowerShell 5. – Joey May 14, 2016 at 8:47 • Additionally, if the array elements are non-static, both get parsed and processed as part of the array creation before the indexing happens and the result is assigned. For example. May 25, 2016 at 20:12 Absolute value With $n=-123


[math]::abs($n)  use $n-replace'-'


Of course, the savings are cancelled if parentheses are needed.

• Or if you need the result on the left side of an operator ;-)
– Joey
Nov 15, 2013 at 16:44

If you need to silence errors, the obvious variant would be

try{ <# something that throws errors #> }catch{}


However, this is way too long. A shorter variant is to run the try block as a script block and just redirect the errors into an unset variable ($null would be the usual one, but that's still too long): .{ <# something that throws errors #> }2>$x


This saves five valuable bytes (if not the world).

Use the $ofs special variable to change the Output Field Separator used when stringifying an array. Useful if you're needing to transform arrays to strings multiple times. For example: $a=1,2,3,4
$a-join',';$a-join','
$ofs=',';"$a";"$a"  Saves 2+n characters on the 2nd -join, where n is the length of the separator, and saves an additional 5+n for the 3rd -join and each thereafter. • Sadly very rarely useful (at least in my golfing so far – I tend to strip it down to only a single join at the end). – Joey Mar 4, 2016 at 20:07 ## Use [bigint] instead of [Math]::floor The bigint datatype is a numeric type that can store arbitrarily large numbers and holds no decimal values. It always floors when using [bigint]::new(). A suffix for it was added in PowerShell core but using the n prefix rounds normally instead of floor which doesn't work for this. Example: [bigint](.9) # 0 [bigint]3/4 # 0 [bigint](.1) # 0 [bigint]1/4 # 0 [bigint]10.5 # 10 [bigint]100/3 # 33 (.9)n # 1 3/4n # 1 (.1)n # 0 1/4n # 0 10.5n # 11 100/3n # 33  • only from PS7+. does not work with TIO yet. because TIO use PS6. docs.microsoft.com/en-us/powershell/module/… Apr 21, 2021 at 18:08 • @mazzy ps5 works for the first method Apr 21, 2021 at 18:30 • yes, it is. $x-$x%1 is better Try it online! :) Apr 21, 2021 at 18:40 • @mazzy that saves bytes when you have defined variables, if you are doing a one-time computation you don't need variables and it saves 3 bytes. Apr 21, 2021 at 18:47 Invoke-Expression and Get-Random can also get pipeline input instead of arguments. For iex this allows to save parentheses on some expressions: iex 1..5-join'+' # won't work iex(1..5-join'+') # does work, but has extra parentheses 1..5-join'+'|iex # doesn't need the parentheses  In case of random this allows a common case to be optimized a bit: random -mi 10 31 # gets a random integer between 10 and 30 10..30|random # much better :-) (random 21)+10 # if needed in another expression that doesn't require extra # parentheses  The latter way of using it simply selects an item from a list. The -c argument can be given to allow more than a single selection. Consider storing repeated script blocks in variables, instead of using functions. I was going to use this to save some characters in my Rock, Paper, Scissors implementation before I realized that re-writing the function as a variable made even the variable unnecessary. This could still be useful for other scripts though, where you're actually running the same code multiple times. function Hi{echo 'Hello, World!'};Hi  vs. $Hi={echo 'Hello, World!'};&$Hi  • Useful for functions that do not take arguments. Otherwise the params(...) would take up more space than the function definition saves. Related: Use filter over function when you can do so. – Joey Nov 26, 2013 at 21:57 • You could use $args to avoid the need for params; i.e. $x={$args[0]+2} (or even $x={2+"$args"}); may save a character or 2 in some circumstances. You can also combine this with another trick for multiple params: $x={$a,$b=$args;$a+$b+3} Jan 1, 2017 at 20:35

### Use variables to store .NET names

As outlined by cogumel0 in this answer, you can use variables to store .NET type names. For example, changing

param($a)$a-[math]::pow(2,[math]::floor([math]::log($a,2)))  into param($a)$a-($m=[math])::pow(2,$m::floor($m::log($a,2)))  saved 3 bytes in this example. You can use $s|% t*y instead [char[]]$s to split a string to char array. Given from TessellatingHeckler's answer: % t*y expands to | ForEach-Object -Method ToCharArray equiv. of "$args".ToCharArray()

For example, compare

$s|% t*y  and [char[]]$s


and

$s.ToCharArray()  It's useful with $args especially: $args|% t*y • That's quite neat. I've used the % trick for members a few times as well, but most of my golfing predates that feature ;-). It's quite a general technique as well: Try finding a letter/wildcard combination that matches the property/method you need (and is shorter than the real thing). – Joey Jul 8, 2018 at 9:36 • Another useful examples from the answer: $s|% *per for $s.toUpper() and $s|% *wer for $s.toLower(). I'm agree that's quite neat. Jul 8, 2018 at 12:19 • One more example: |% t* "ddd\:hh\:mm\:ss" for [TimeSpan].toString("ddd\:hh\:mm\:ss") Jul 8, 2018 at 12:42 • Well, that's just a waste of quotes, then; you don't need them here :-) – Joey Jul 8, 2018 at 17:54 • Dec 27, 2018 at 21:01 You can access static methods via instance: $x=4,2,3
$x::Sort($x) # [Array]::Sort($x) "$x" # 2 3 4
$x::Reverse($x) # [Array]::Reverse($x) "$x" # 4 3 2


BigInteger suffix (n) introduced in PowerShell 7:

(0n)::Pow(4,5) # [bigint]::Pow(4,5)
(0n)::Abs(-3) # [bigint]::Abs(-3)
2*0n::Abs(-3) # some cases don't require parentheses

• Thanks. n suffix introduced in PS7. So you should notice about it. $x::Reverse($x) does not work for me Feb 16, 2021 at 6:16
• @mazzy It works on TIO Feb 16, 2021 at 7:36
• TIO is highly customized enironment. moreover, powershell for windows is different from powershell for linux (see sort alias). Please check your advises with local environment. Feb 16, 2021 at 7:53
• It works in all environments that i have: 7.1.1 Core (Windows), 5.1 (Windows), code.golf (7.1.2 Core (Linux)). Feb 16, 2021 at 8:27
• Oh, that's convenient and news to me. Nice.
– Joey
Feb 17, 2021 at 8:09

### Use Boolean logic in place of if-counters in a loop

Suppose you're adding all even numbers from 1 to 10 ... 2+4+6+8+10=30

1..10|%{if($_%2-eq0){$o+=$_}};$o


You could use Boolean negation to shorten it to

1..10|%{if(!($_%2)){$o+=$_}};$o


to save a byte, but how about instead use implicit casting of Booleans to ints, and roll the conditional into the accumulator

1..10|%{$o+=$_*!($_%2)};$o


Saves 6 bytes in this example.

• Jesus christ. I'm planning to do this in my production code at work, my colleagues will love me for it. Nov 4, 2016 at 9:13