A lot of time has passed since I have organized a PowerShell oneliner contest. So when I saw the post by fellow MVP and scripting champion Mike F Robbins on a PowerShell Function to Determine Available Drive Letters, I thought that it could be fun to organize a contest to see who can manage to write the shortest possible oneliner that achieves the same result as Mike's function.
As you can see reading his blogpost, the function accepts parameters such as -Random, to return one or more available drive letters at random, or -All, to return all the available drive letters. It also allows you to exclude some letters from the match (A, B, C, D, E, F and Z) by means of a -ExcludeDriveLetter parameter.
Now, for this specific contest, what I want to get in a comment to this post is:
- a oneliner (meaning in particular no semi-colon) that
- returns one and only one random available drive letter on the system where it runs
- with the exception of A-F and Z
- whose object type is a System.String (I'll check this Get-Member)
- and whose formatting is, say, G: or h: (case doesn't matter, we are on Windows)
For sure
- aliases are mandatory, meaning that you can't use a cmdlet unless it has an alias
- backticks are accepted for readability
- you can use every PowerShell version, including 5.1, just state in the comment what version you tested it with
- should you find a shorter oneliner to solve a task you are allowed to post additional comments (just remember to sign your comments so that I know who's who and so that I can get in touch with the winner)
A few more rules:
- Entries (comments) will not be made public until after the submission deadline.
- The first person to produce the shortest working solutions to the task wins.
- The winner will be announced on Friday, November 4th on this blog.
- I'll be the only judge.
If you want to spread the word about this PowerShell contest, feel free to twit about it. You can use the hashtags #poshcontest2016 and #powershell so that other competitors can share their thoughts (not the solutions of course!).
UPDATE Nov 4 2016
We have a winner! Check it here.
UPDATE Nov 4 2016
We have a winner! Check it here.
97..122 | % {$($_ -as [Char])} | ? {$_ -notmatch '([a-f]|z)'} | % { if ($_ -notin (Get-PSProvider -PSProvider FileSystem).Drives.Name -as [Array]) {'{0}:' -f $($_ -as [String]).ToUpper()} } | Get-Random
ReplyDeleteTesting private comment: jeff.jerousek
ReplyDeleteSubmission: Powershell v5:
ReplyDelete(diff((ls Function:[f-x]: -Name)-replace".$")(gdr).Name|?{$_.sideIndicator-eq"<="}|Random|select -exp I*)+":"
Jeff.Jerousekatgmail
71..89|%{if(!(ls "$([char]$_):" -ea 4)){@{([char]$_+":")=[Guid]::NewGuid().GetHashCode()}.GetEnumerator()}}|sort value|select -exp name -f 1
ReplyDelete([char[]](71..89) -replace '$',':\' | ?{(gwmi win32_volume).name -notcontains $_} | Get-Random) -replace '\\'
ReplyDeletePowerShell 5.1 one-liner to get a random and free drive Letter from G..Y in 79-Chars by @roger_zander:
ReplyDelete(([char[]](71..89))|?{(gdr|? Used).Name-notcontains$_}|sort{Get-Random})[0]+":"
If you want a RANDOM available letter, you could use:
ReplyDeletewhile(gdr($l=[char](Get-Random -Mi 69 -Ma 90))-ea 0){}$l
If you want the FIRST available(shorter):
for($i=70;gdr($l=[char]++$i)2>0){}$l
Found an even shorter version for the random:
ReplyDelete(ls function:[g-y]:).name|?{!(test-path $_)}|random
I'm pretty sure you can go shorter than that, but just to have good baseline...:
ReplyDelete71..89|%{"$([char]$_):"}|?{!(ls $_\* -ea 0)}|random
Fixing typo:
ReplyDelete(diff((ls Function:[g-x]: -Name)-replace".$")(gdr).Name|?{$_.sideIndicator-eq <="}|Random|select -exp I*)+":"
Jeff Jerousek
I assume you're ok with throwing lots of errors as it's not mentioned. I'm using 5.1
ReplyDelete([char[]](71..90)|?{!(gdr $_)})[0]+':'
(71..89|%{[char]$_+":"}|%{if($_-NotIn(gwmi win32_logicaldisk).DeviceID){$_}})[[System.Random]::new().Next(0,(71..89|%{[char]$_+":"}|%{if($_-NotIn(gwmi win32_logicaldisk).DeviceID){$_}}).length)]
ReplyDeleteif randomness is a must:
ReplyDelete([char[]](71..90)|?{!(gdr $_)})[(irm http://v.ht/7WBh)]+':'
or, if v.ht is too ridiculous for you:
([char[]](71..90)|?{!(gdr $_)})[[random]::new().Next()%19]+':'
Why isn't there a built in alias for get-random by the way? frustrating.
Assuming `random` for `get-random` satisfies your mandatory aliases requirement, this does it in 126 characters, albeit slowly:
ReplyDelete'GHIJKLMNOPQRSTUVWXY'.ToCharArray()|where{(gwmi win32_logicaldisk|%{$_.ToString().ToCharArray()[-3]}) -NotContains $_}|random
whoops, forgot to add the colon, which requires an extra four characters, totaling 132:
ReplyDelete('GHIJKLMNOPQRSTUVWXY'.ToCharArray()|where{(gwmi win32_logicaldisk|%{$_.ToString().ToCharArray()[-3]}) -NotContains $_}|random)+':'
(71..89 | %{[string][char] $_}) | ?{(gdr -PSProvider 'F*').Name -notcontains $_} | %{"{0}:" -f $_} | random
ReplyDelete-PittsburghTech
# Tested on version 5.1
ReplyDelete([char[]](71..89)|?{$_-notin(gdr).name}|random)+':'
@mickyballadelli
ReplyDelete,((ls -Pa Function:[a-z]: -N) -match "['G-Y']")|%{$_[[Random]::new().Next(0,$_.count)]}
88 chars
ls function:[g-y]: -n|?{!(gci $_)}|random
ReplyDeleteJust a bit of fun,
Seroko@gmail.com
@mickyballadelli
ReplyDelete[char](,(71..89|?{![io.file]::exists("$($_):")})|%{$_[[Random]::new().Next(0,$_.count)]})+':'
94 chars
Powershell V5
Could you please define 'random' in as much detail as you can?
ReplyDeleteScript created by Chris O'Neill, I can be contacted via email at chris.oneill {@} wildhorseresort {dot} com
ReplyDeleteI am a novice at powershell but I'm nothing if not a Next-pert so I tried to use that to my advantage. The script below is my entry to the Powershell Oneliner Contest 2016. This script grabs paths A-Z, outputs this to a grid-view where the user selects G-Y so as to exclude A-F and Z. After this list is passed through get-random will select a random drive letter to be presented.
$drive = Get-ChildItem -Path Function:[a-z]: -Name | Out-GridView -PassThru | get-random
$drive
# ps 5.1
ReplyDelete@($d=(gdr).Root.Trim('\')|?{$_ -match'^[^A-F,Z]:'})|%{}{}{$d[([random]::new().Next(0,$d.count))]}
(gwmi –Query "SELECT * from win32_logicaldisk"|where {$_.DeviceID -match "[G-Y]"}).DeviceID[(20/(gci).GetHashCode())]
ReplyDeleteI've re-written my entry slightly. Available to contact at chris.oneill {@} wildhorseresort {dot} com.
ReplyDeleteGet-ChildItem -Path Function:[a-z]: -Name | out-gridview -PassThru | get-random
Tested on 5.1 but should work on any version down to 3.0 (compare-object). It is slightly longer than "needed" due to .ToUpper() but I would argue that if you can't handle upper and lower case volume names then you didn't succeed.
ReplyDeletedo{$i=([random](date).Second).Next(71,90)|diff ((gdr -psp FileSystem).Name.ToUpper().ToCharArray()|%{[int]$_})|?{$_.SideIndicator -eq "=>"}|%{[char]$_.InputObject+":"}}until($i)$i
ls function:[g-y]: -n | ?{ !(test-path $_) } | random
ReplyDeleteAttempt 2.
ReplyDeleterandom((gwmi Win32_LogicalDisk).DeviceID-match"[G-Y]")
- Gavin Eke
Okay I think this will be my last post. 1 less character then attempt 2.
ReplyDelete(gwmi Win32_LogicalDisk).DeviceID-match"[G-Y]"|random
I wish I could get it shorter.
ReplyDelete(,(ls function:[g-y]: -n|?{$_-notin(gwmi win32_volume).driveletter}))|%{$_[([random]([datetime]::now.second)).next($_.count)]}
- /u/Taylor_Script
<#
ReplyDeleteHello. Please disregard my previous entry.
This is my newest submission.
#>
#Random available drive letter. 55 Characters
ls Function:[G-Y]: -N|?{$_[0] -notin (gdr).Name}|random
#Selects first available drive. 60 Characters
ls Function:[G-Y]: -N|?{$_[0] -notin (gdr).Name}|select -F 1
<#
Powershell 5.0
Cheers,
Briggzee
#>
do{$d=([char](random 85 -mi 71)+':')}until(!(cd($d)2>0))$d
ReplyDeletedo{$d=([char](random 85 -mi 71)+':')}until(!(cd($d)2>0))$d
ReplyDeleteCheers, Javy de Koning
Darn, forgot to update the max int, final answer:
ReplyDeletedo{$d=([char](random 89 -mi 71)+':')}until(!(cd($d)2>0))$d
Cheers,
Javy de Koning
If we aren't allowed errors in output
ReplyDelete((71..89)|%{[char]$_}|%{if(!(dir $_":" -ErrorAction Ignore)){$_}}|get-random)+":"
If you don't mind errors
((71..89)|%{[char]$_}|%{if(!(dir $_":")){$_}}|get-random)+":"
Tested on Powershell 5.0
Cheers,
LatexGolem
irm 'https://www.random.org/integers/?num=1&min=70&max=88&col=1&base=10&format=plain&rnd=new'|%{for($j=$_;gdr($d=[CHAR]++$J)2>0){}$d +":"}
ReplyDeleteirm 'https://www.random.org/integers/?num=1&min=70&max=88&col=1&base=10&format=plain&rnd=new'|%{for($j=$_;gdr($d=[CHAR]++$J)2>0){}$d +":"}
ReplyDeleteTested with powershell 4.0
Rob Hagman
((71..89|%{"$([char]$_):"|?{-not(gi $_ -EA Ig)}})|sort{[guid]::newguid()})[0]
ReplyDelete[char[]](71..89) | ?{(gdr).Name -notcontains $_} | %{"{0}:" -f $_} | Get-Random
ReplyDeleteAre spaces counted? If so,
[char[]](71..89)|?{(gdr).Name-notcontains$_}|%{"{0}:"-f$_}|Get-Random
-PittsburghTech
Attempt 2 (tested 5.1):
ReplyDelete((gdr -psp FileSystem).Name.ToCharArray()|%{[int]$_}|?{$_ -ge 71}|diff (71..89)).InputObject|random|%{[char]$_+":"}
[char[]](65..90)|?{(gdr).Name-notcontains$_}|%{$_+":"}|Get-Random
ReplyDelete-PittsburghTech
Sorry, had old numbers in before.
ReplyDelete[char[]](71..89)|?{(gdr).Name-notcontains$_}|%{$_+":"}|Get-Random
-PittsburghTech
([char[]](71..89)|?{!(gdr $_)2>0}|sort{[guid]::newguid()})[0]+':'
ReplyDeleteTested on PowerShell 5.1. If catching errors doesn't matter, we could remove 3 characters and take it down to this:
([char[]](71..89)|?{!(gdr $_)}|sort{[guid]::newguid()})[0]+':'
[char[]](71..89)|?{!(gdr($_)2>0)}|%{$_+":"}|Get-Random
ReplyDelete-PittsburghTech
([char[]](65..89)|?{!(gdr($_)2>0)}|Get-Random)+":"
ReplyDelete-PittsburghTech
Sorry, I don't know if i had the right numbers. Same as last one, but has right number range.
ReplyDelete([char[]](71..89)|?{!(gdr($_)2>0)}|Get-Random)+":"
-PittsburghTech
hello, this is my contribution :
ReplyDelete((71..89|%{[char]$_})|?{$_-NotIn(gdr).Name}|Get-Random)+':'
seem to work on PS2+
Ooops too fast to post, another contrib :
ReplyDelete([char[]](71..89)|?{$_-NotIn(gdr).Name}|Get-Random)+':'
seem to work on Ps2+
(random ([char[]](71..89)|?{!(gdr $_ -ea 0)}))+":"
ReplyDelete([char[]](71..89)|?{!(gdr $_ -ea 0)})[([random]::new()).next($(([char[]](71..89)|?{!(gdr $_ -ea 0)})).count)]+":"
ReplyDeleteben@himsel.io
gci function:[g-y]: -n|?{!($_|ls -ea 4)}|random
ReplyDeleteTested with 5.0 but should work in 3+, maybe 2 as well.
In this case, random is not really an alias; it's using PowerShell's behavior of prepending Get- to a command if it can't find a match any other way. I may try something else and submit it as a different answer if this doesn't count (it's not clear from your post).
gci function:[g-y]: -n|?{!($_|ls)}|random
ReplyDeleteTested with 5.0 but should work in 3+, maybe 2 as well.
Here's a shorter version of my last submission. It throws non-terminating errors, but that's a different stream and the output of this is the same. Not sure if that makes it invalid or not.
That is at all not my idea - but I redisgned the oneliner to match the criteria. Mostly in many cases we have to reuse code and redisgne it for our goal. But I have to metion that, otherwise I will feel guilty.
ReplyDeletels function:[g-y]: -n | ?{ !(test-path $_) } | random
<#
ReplyDeletethis script uses get-random that doesn't have a alias
created on powershell version 4.0
written by Sjef.poppelaars@centric.eu
#>
[char[]](71..89)|?{$_-notin(gdr).name}|%{"${_}:"}|get-random
<#
ReplyDeletethis script uses a workarround for get-random that doesn't have default alias, the new alias name is d
the cmdlet get-random is not used until it has the alias d
created on powershell version 4.0
written by Sjef.poppelaars@centric.eu
#>
[char[]](71..89)|?{$_-notin(gdr).name}|% -begin{sal d get-random}-process{$p+=@("${_}:")}-end{$p|d}
($a = (gdr -PSProvider 'filesystem').name | ? {$_ -in [char[]](71..89)})[[DateTime]::Now.Ticks%($a.count)]
ReplyDelete($a=(gdr -PSProvider "filesystem").name|?{$_-in[char[]](71..89)})[[DateTime]::Now.Ticks%($a.count)]
ReplyDeleteEntry:
ReplyDelete[char[]](71..90)|%{if($_-ne'Z'-and!(ls($_+':')-EA Ignore)){$_+':'}}|Get-Random
Tested under PSVersion 5.0.10514.6
- @aaspnas
($a=(gdr -P "f*").name|?{$_-in[char[]](65..90)})[[DateTime]::Now.Ticks%($a.count)]
ReplyDelete($a=(gdr -P "f*").name|?{$_-in[char[]](71..89)})[[DateTime]::Now.Ticks%($a.count)]
ReplyDeletels function:[g-y]: -n|?{!($_|ls -ea 4)}|random
ReplyDeleteor
ls function:[g-y]: -n|?{!($_|ls)}|random
Same as my last two, but saved a byte using ls instead of gci in the first call (duh, how didn't I see that before).
PS ISE 5.1:
ReplyDeletebegin{$x=71..89|?{!(ls -ea 0 "$([char]$_):")}}end{"$([char]$x[[random]::new().Next(0,$x.count)]):"}
([char[]](71..89)|?{$_-notin(gdr -P "f*").Name}|sort{[guid]::NewGuid()})[0]+':' Version5.0
ReplyDeleteTHD
<#
ReplyDeletethis script uses a workarround for get-random that doesn't have default alias, the new alias name is d
the cmdlet get-random is not used until it has the alias d
Versie 2 reduced size of line
created on powershell version 4.0
written by Sjef.poppelaars@centric.eu
#>
[char[]](71..89)|?{$_-notin(gdr).name}|%{$p+=@("${_}:")}-b{sal d get-random}-en{$p|d}
Best I could do:
ReplyDelete(ls function:*:)-notMatch"[A-F,Z]"|Random|% na*
/Tore
now, if I cheat:
ReplyDelete(ls function:[g-y]:)|random|% Na*
/Tore
(ls Function:[G-Y]: -N|?{!(ls $_ 2>null)})[0]
ReplyDeleteUpdated: An even shorter version as i noticed z is in sequence...
ReplyDelete[char[]](71..89)|%{if(!(ls($_+':')-EA Ignore)){$_+':'}}|Get-Random
If you can't use Get-Random, as it has no alias...
[char[]](71..89)|sort{[guid]::NewGuid()}|%{if(!(ls($_+':')-EA Ignore)){$_+':'}}|select -F 1
@aaspnas
PS ISE 5.0:
ReplyDelete(nal x(gcm g*om)),"$(x(71..89|?{!(gdr [char]$_)}|%{[char]$_})):"
((nal x(gcm g*om)),"$(x(71..89|?{!(gdr [char]$_)}|%{[char]$_})):")[1]
ReplyDelete([Random]::new()).Next(0,(($l=(gwmi Win32_logicalDisk).DeviceID -notmatch "[A-F|Z]").Length)) | % {$l[$_]}
ReplyDeletehaffimt@gmail.com
#for v5.1
ReplyDelete([char[]](71..89)|?{$_-notin(gdr|% N*)}|random)+":"
([Random]::new()).Next(0,(($l=(gwmi Win32_logicalDisk).DeviceID -notmatch"[A-F|Z]").count)|%{$l[$_]}
ReplyDeletehaffimt@gmail.com
[char](,(71..89|?{!(gi "$([char]$_):" -ea Si) })|%{$_[[Random]::new().Next($_.count)]})+':'
ReplyDelete92 chars
PS V5
@mickyballadelli
The no cmdlet w/o aliases rule is evil. Makes shuffling a list quite long, as well as figuring out how to get drive letters.
ReplyDelete((gwmi win32_logicaldisk|% n*e|sort{[guid]::NewGuid()})-match'[g-y]')[0]
72 characters, first attempt while having a cold. Will try to get better :)
# v5.1
ReplyDelete($l=[char[]](71..89)|?{$_-notin(gdr|% N*)})[[random]::new().Next($l.count)]+":"
Hello,
ReplyDeleteanother one, without get-random.
,([char[]](71..89)|?{$_-NotIn(gdr).Name})|%{$_[[Random]::new().Next(0,$_.count)]+':'}
Tested on PS5
#PowerShell 5.1 onliner to get a random and free drive Letter from G..Y in 77-Chars by roger_zander:
ReplyDelete(([char[]](71..89))|?{(gdr|? U*).Name-notcontains$_}|sort{Get-Random})[0]+":"
[char]((103..121).where{!(test-path([char]$_+':'))}|Get-Random)+':'
ReplyDelete(ls function:[g-y]:|%{$_|select -ov x|%{try{sl $x.name -ea st}catch{write @{[guid]::NewGuid()=$x.name}}}}|sort Keys)[0].values
ReplyDeletePoSH: 5.0.10586.494
ls function:[g-y]: -n|?{!(ls $_ 2>0)}|random
ReplyDeletethe same thing as gihub gist: https://gist.github.com/nohwnd/5958eda5c9f6459c3cbe8acb6b7bfe25
ReplyDelete$oneliner = {(103..121|%{if(cd($d="$([char]$_):")2>&1){$d}})[0]}
Describe "One liner contest" {
It "contains no semicolons" {
$oneliner | Should Not Match "\;"
}
It "outputs single string" {
(&$oneliner) -is [string] | Should be $True
}
It "Outputs single letter followed by colon" {
&$oneliner | Should Match "^[a-z]\:$"
}
It "Should not output drive letter " -TestCases `
@{DriveLetter = "a:"},
@{DriveLetter = "b:"},
@{DriveLetter = "c:"},
@{DriveLetter = "d:"},
@{DriveLetter = "e:"},
@{DriveLetter = "f:"},
@{DriveLetter = "z:"}{
param ($DriveLetter)
&$oneliner | Should Not Be $DriveLetter
}
It "Resulting drive should not exist" {
&$oneLiner | Should Not Exist
}
}
"Length: " + $oneliner.ToString().Length
"Result: " + (&$oneliner)
50 characters, Tested on PowerShell 5. I am assuming default settings of error action preference (continue).
@nohwnd
... And an even shorter version now that i have had time to think...
ReplyDelete[char[]](71..89)|%{$_+':'}|?{(!(ls($_)-EA 0))}|Get-Random
If Get-Random is not allowed (as not an alias):
[char[]](71..89)|%{$_+':'}|?{(!(ls($_)-EA 0))}|sort{[guid]::NewGuid()}|select -F 1
@aaspnas
# @FooBartn
ReplyDelete# PSVersion 5.1.14393.206
ls Function:[a-z]: -N | ? {
$_ -notmatch '[A-F]|[Z]'
} | ? {
$_ -notin (gwmi win32_logicaldisk).DeviceId
} | random
In case the whitespace is an issue:
ReplyDelete# @FooBartn
# PSVersion 5.1.14393.206
ls Function:[a-z]: -N | ?{$_ -notmatch '[A-F]|[Z]'} | ?{$_ -notin (gwmi win32_logicaldisk).DeviceId} | random
ls function:[g-y]: -n|?{!(gci $_ -ea 0)}|random
ReplyDeleteThis is my final. I'm not sure I put it here yet or not.
ReplyDelete([char[]](71..89)|?{!(gdr($_)2>0)}|sort{[guid]::newguid()})[0]+":"
-PittsburghTech
do{$a="$([char]([random]::new().Next(71,90))):"}while(gwmi win32_logicaldisk -F "deviceid='$a'")$a
ReplyDeleteTested on PS 5.1
Twitter handle: @PSAdm
Update to last entry, and saved a character:
ReplyDeletedo{$a="$([char]([random]::new().Next(71,90))):"}while(gwmi win32_logicaldisk|? deviceid -eq $a)$a
127 characters. I know it won't win, so I'm curious what others are doing here. Tested on PS5.1.
ReplyDelete"$((71..89|%{[char]$_}|?{$_-notin(gdr|?{$_.name.length-eq1}|select -exp name)}|tee -v a)[Random]::New().Next(0,$a.count-1)]):"
If not too late by now, I managed to shorten it by two characters:
ReplyDelete''+(gwmi win32_logicaldisk|% n*e|sort{[guid]::NewGuid()}|sls [g-y])[0]