Return to a previous line in a script

by Typeo at 2013-03-18 13:55:52

Sorry for making a new thread for such a simple question, however I have been googling for the past couple hours and looking through the index of one of my powershell books and can not find the answer.

Basically, is there a way I can have a line of code run to start over back at a previous line of in a script? Sort of like how a Do / Until loop works except for not a do loop. I know there are ways to do this in other languages, but I have not ran across it in PowerShell and am not even sure if you can accomplish such a thing.

If you have any idea what this is called or how to accomplish it, it would be a huge help.

Thanks,
Typeo
by mjolinor at 2013-03-18 15:52:51
There’s a couple of different ways to do that. Can you post a code sample you’d like to use for demo?
by Typeo at 2013-03-18 18:28:08
Its currently on my work machine, so I don’t have access to it currently. However, until I get the code to post tomorrow, what is the process of doing this called?
by mjolinor at 2013-03-18 18:45:24
It’s called "posting code".
Use the Code blocks from the HTML buttons at the top of the comment box.
Paste your code between the ] of the opening tag and the [ of the closing tag.
by Typeo at 2013-03-19 07:10:47
[quote="mjolinor"]It’s called "posting code".[/quote]

Haha. I meant the process of returning to a previous line in the script was called. Not what posting code was. :slight_smile:

Anyways, here is the code I am working with.

# Lets do this.

$pw = convertto-securestring -AsPlainText -Force -String ‘Pass1234’
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist "Company\TMNT",$pw

$sb =
{
#Imports the LocalAccounts Module from C:/Users/Username/Documents/WindowsPowerShell/Modules
Import-Module LocalAccounts

#Sets the Username Variable
$Username = Read-Host ‘Enter Prefered Username.’

if ((Get-LocalUser -all | Where-Object {$.name -eq $Username} | Select-Object -ExpandProperty Name) -eq ($Username) -or (!$Username))
{
#Do, Until loop that checks whether the the username is unique and is not Null
Do
{
$Username = Read-Host ‘That username already exists!’
}

Until
((Get-LocalUser -all | Where-Object {$
.name -eq $Username} | Select-Object -ExpandProperty Name) -ne ($Username) -and ($Username))
}

#sets the Password varible
$Password = Read-Host ‘Enter Username Password’


#Creates the new user with the specified Username and Password variables
New-LocalUser -Name $Username -Password $password -ErrorAction ‘silentlycontinue’

if ((Get-LocalUser -all | Where-Object {$.name -eq $Username} | Select-Object -ExpandProperty Name) -eq ($Username))
{
Read-Host ‘New User Has Been Created, Press Enter to Continue!’
}

Else
{
Read-Host ‘User was not able to be created, Press enter to try again..’
}
}

Invoke-Command -ComputerName DC-Jeryn -Credential $cred -ScriptBlock $sb


What I am trying to do is make it where if the last "Else" command runs I would like it to return to the previous line where it initially asks you for a username.
by mjolinor at 2013-03-19 07:55:25
Having this in a script block makes it very easy to use recursion:

# Lets do this.

$pw = convertto-securestring -AsPlainText -Force -String 'Pass1234'
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist "Company\TMNT",$pw

$sb =
{
#Imports the LocalAccounts Module from C:/Users/Username/Documents/WindowsPowerShell/Modules
Import-Module LocalAccounts

#Sets the Username Variable
$Username = Read-Host 'Enter Prefered Username.'

if ((Get-LocalUser -all | Where-Object {$
.name -eq $Username} | Select-Object -ExpandProperty Name) -eq ($Username) -or (!$Username))
{
#Do, Until loop that checks whether the the username is unique and is not Null
Do
{
$Username = Read-Host 'That username already exists!'
}

Until
((Get-LocalUser -all | Where-Object {$.name -eq $Username} | Select-Object -ExpandProperty Name) -ne ($Username) -and ($Username))
}

#sets the Password varible
$Password = Read-Host 'Enter Username Password'


#Creates the new user with the specified Username and Password variables
New-LocalUser -Name $Username -Password $password -ErrorAction 'silentlycontinue'

if ((Get-LocalUser -all | Where-Object {$
.name -eq $Username} | Select-Object -ExpandProperty Name) -eq ($Username))
{
Read-Host 'New User Has Been Created, Press Enter to Continue!'
}

Else
{
Read-Host 'User was not able to be created, Press enter to try again…'
.$sb
}
}

Invoke-Command -ComputerName DC-Jeryn -Credential $cred -ScriptBlock $sb
by Typeo at 2013-03-19 08:53:25
Entering in the .$sb returns this error

The expression after ‘.’ in a pipeline element produced a object that was not valid. It must result in a command name, script block or CommandInfo object.
+ CategoryInfo : InvalidOperation: (:slight_smile: , RuntimeException
+ FullyQualifiedErrorId : BadExpression
by MasterOfTheHat at 2013-03-19 09:15:29
[quote="Typeo"]Entering in the .$sb returns this error

The expression after ‘.’ in a pipeline element produced a object that was not valid. It must result in a command name, script block or CommandInfo object.
+ CategoryInfo : InvalidOperation: (:slight_smile: , RuntimeException
+ FullyQualifiedErrorId : BadExpression[/quote]
?? Looks like you were trying to run ".$sb" at the console?
Save the whole script as a .ps1 file and then run the .ps1 from the console.
by Typeo at 2013-03-19 09:23:39
I tried running it in the ISE (which is where i was able to copy that error report) as well as right clicking the actual ps1 file and "run with powershell’.

Both produced the error… I also tried . $sb

Is there another way it should be ran?

Edit:

Does it matter the Script Block we are calling with the .$sb is not actually defined inside of said script block? We are instead calling the SB that is currently running the command?
by Typeo at 2013-03-19 09:36:51
Could it be that $sb is just a variable basically (Correct?).

When we call .$sb, we get error report stating "The expression after ‘.’ in a pipeline element produced a object that was not valid. It must result in a command name, script block or CommandInfo object."

So it is looking for a Command name, Script block, or Commandinfo object, and we are feeding it a variable?
by MasterOfTheHat at 2013-03-19 09:58:55
EDIT:: Sorry, Typeo! I wasn’t looking at the entire script! I see where it makes the .$sb call now… Disregard the post below.







Still not understanding why you are "calling" .$sb…

As a very simple example, here’s a script that does the same thing mjolinar’s script does, as far as the invoke-command and script-block goes:
$sb = {
$comp = Get-ADComputer JXTOOLS01
$comp.Name
}

Invoke-Command -ScriptBlock $sb

That script is saved as recursion.ps1, and then you just run the script using ".\recursion.ps1":
PS F:> .\recursion.ps1
JXTOOLS01

What are we misunderstanding?
[quote]
So it is looking for a Command name, Script block, or Commandinfo object, and we are feeding it a variable?[/quote]
To answer this question, the -ScriptBlock parameter is just looking for a string that begins with ‘{’, ends with ‘}’, and contains a series of powershell commands between them. If that is an actual literal string inline on the Invoke-Command command or if it is a variable containing the appropriate string, or if it is some combination thereof, powershell will translate it and pass it to Invoke-Command for execution.

PS F:\Storage\Scripts\Windows\Junk> help Invoke-Command -param scriptblock

-ScriptBlock <ScriptBlock>
Specifies the commands to run. Enclose the commands in braces ( { } ) to create a script block. This parameter is
required.

By default, any variables in the command are evaluated on the remote computer. To include local variables in the
command, use the ArgumentList parameter.

Required? true
Position? 1
Default value
Accept pipeline input? false
Accept wildcard characters? false
by mjolinor at 2013-03-19 10:00:43
I missed that it’s a remote invocation.
Try this:
# Lets do this.

$pw = convertto-securestring -AsPlainText -Force -String 'Pass1234'
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist "Company\TMNT",$pw

$sb =
{
$sb1 =
{
#Imports the LocalAccounts Module from C:/Users/Username/Documents/WindowsPowerShell/Modules
Import-Module LocalAccounts

#Sets the Username Variable
$Username = Read-Host 'Enter Prefered Username.'

if ((Get-LocalUser -all | Where-Object {$.name -eq $Username} | Select-Object -ExpandProperty Name) -eq ($Username) -or (!$Username))
{
#Do, Until loop that checks whether the the username is unique and is not Null
Do
{
$Username = Read-Host 'That username already exists!'
}

Until
((Get-LocalUser -all | Where-Object {$
.name -eq $Username} | Select-Object -ExpandProperty Name) -ne ($Username) -and ($Username))
}

#sets the Password varible
$Password = Read-Host 'Enter Username Password'


#Creates the new user with the specified Username and Password variables
New-LocalUser -Name $Username -Password $password -ErrorAction 'silentlycontinue'

if ((Get-LocalUser -all | Where-Object {$_.name -eq $Username} | Select-Object -ExpandProperty Name) -eq ($Username))
{
Read-Host 'New User Has Been Created, Press Enter to Continue!'
}

Else
{
Read-Host 'User was not able to be created, Press enter to try again…'
.$sb1
}
}
.$sb1
}
Invoke-Command -ComputerName DC-Jeryn -Credential $cred -ScriptBlock $sb
by Typeo at 2013-03-19 11:38:47
@MasterOfTheHat - Thanks for such a long and detailed post, sorry i wasn’t more clear on what I was asking for so it wouldn’t of gone to waste.

@mjolinor - This actually works perfectly. It restarts back the the beginning of the $sb1 script block whenever I hit the specified ELSE statements. I am still trying to wrap my head around why this worked, but the first suggestion did not, but it works and that is what I was going for.

It also might not make much since why I would want it to do that, however, at the time I made the post this was all the code. Since then I have tripled the size and added a few more "features" in it that makes it flow a lot better as this pulls it back out to the main menu.