Powershell randomly select command to execute

Viewed 300

Idea: I want to change the time randomly using set-date's three options, .AddHours(), .AddMinutes(), .AddSeconds(). My first thought was storing them in an array and referencing them randomly but it's not executing. It's just storing the string printing instead of executing it.

Code so far:

$test = "Set-Date -Date (Get-Date).AddHours($\(Get-Random -Maximum 25))", "Set-Date -Date (Get-Date).AddSeconds($\(Get-Random -Maximum 61))", "Set-Date -Date (Get-Date).AddMinutes($\(Get-Random -Maximum 61))"

Output:

$Test 
Set-Date -Date (Get-Date).AddHours(22)
Set-Date -Date (Get-Date).AddSeconds($\(Get-Random -Maximum 61))
Set-Date -Date (Get-Date).AddMinutes($\(Get-Random -Maximum 61))
$test[0]
Set-Date -Date (Get-Date).AddHours(22)
$(Get-Random -InputObject $test) 
Set-Date -Date (Get-Date).AddMinutes($\(Get-Random -Maximum 61))

If there is another way to do this, if you need further explanation, or if there are any other questions, feel free to ask :) THANK YOU!

4 Answers
  • Use script blocks ({ ... }) to store arbitrary commands in variables. Invoke script blocks on demand with &, the call operator.

  • A few asides:

  • $(...), the subexpression operator is only needed (a) inside expandable strings ("...") and generally to enclose multiple statements; outside of an expandable string, as part of an expression, you do not need it for single commands such as Get-Random -Maximum 25 - enclosing the command in (...)` is enough.

  • &, the call operator, executes a command name/path stored in a string, not a whole command line (a command plus arguments). It is also used to invoke script blocks.

Note: I've replaced Set-Date with Get-Date in the commands below so that experimenting with them doesn't cause side effects.

# Store the commands in an array of script blocks.
$test = { Get-Date -Date (Get-Date).AddHours((Get-Random -Maximum 25)) }, 
        { Get-Date -Date (Get-Date).AddSeconds((Get-Random -Maximum 61)) }, 
        { Get-Date -Date (Get-Date).AddMinutes((Get-Random -Maximum 61)) }

# Select a random command and execute it using &
& (Get-Random $test)  

Performance note: For better performance, the array of input objects to randomly select an element from, $test, is passed as a direct argument here (implicitly binds to the -InputObject parameter) rather than via the pipeline ($test | Get-Random). For small arrays, the difference will be negligible, but with larger ones it matters. Do note, however, that cmdlets often do not accept arrays as a whole as arguments, in which case the pipeline must be used - see GitHub issue #4242.

Here's some ways to do this that don't involve generating code in strings.

First, let's select a random method of the 3 methods you want, then invoke it!

$now = Get-Date
$value = Get-Random -Maximum 61
$method = $now.AddSeconds,$now.AddMinutes,$now.AddHours | Get-Random
Set-Date -Date $method.Invoke($value)

This doesn't account for different maximums depending on time unit, but it may give you some food for thought.

Consider also that a DateTime object has an .Add() method that takes a [TimeSpan] object, so you could also just pre-calculate a timespan, then use $now.Add($timespan).

It's not clear from your question why you want to alternate between hours minutes and seconds, so if I make the assumption that you're trying to just change the date to be some random amount of time in the future that's less than or equal to 24 hours, your issue becomes much simpler:

$offset = Get-Random -Maximum 86400   # or 86401 if you want
$timespan = New-TimeSpan -Seconds $offset
Set-Date -Adjust $timespan

and you can see in this case that you can use the -Adjust parameter instead of getting the current date and manually calling its .Add() method.

If you must toggle between offset units, let's try choosing random hashtables and using those as parameters to New-TimeSpan:

$chaos = @(
    @{ Seconds = (Get-Random -Maximum 61) } ,
    @{ Minutes = (Get-Random -Maximum 61) } ,
    @{ Hours = (Get-Random -Maximum 25) }
) | Get-Random
$timespan = New-TimeSpan @chaos
Set-Date -Adjust $timespan

You need to use the invocation operator on the string. Or, I do not fully understand your requirements.

& $test[0]

You can use, invoke-expression/command.

Related