Background:
This is a parameter validation, its being run on powershell 2.0
there is no option to upgrade the WMF. My problem is...
if both parameters are empty, then display the Specify a parameter, if only P1 is empty display another if P2 is is empty then display another and if both are occupied, display another. I can't find a way to make it work right...
please help
Function checkParam
{
write-verbose "Parameter Validation"
If ("Lzu","Mlv","Irv" -Contains $MoveDBsTo)
{$param1=1}
else{$param1=0}
If (1,2,3 -Contains $SelectedPref)
{$param2=1}
else{$param2=0}
If ($param1 -and $param2 -eq 0)
{
$ErrorParams4 = #{
Message="SYNTAX ERROR: `n Specify a parameter"
Category="InvalidData"
}
Write-error #ErrorParams4
$paramsEmpty=1
Exit
}
If ($param1 -eq 0 -and $param2 -ne 1)
{
$ErrorParams1 = #{
Message="SYNTAX ERROR: `n Check syntax: -MoveDBsto [Lzu, Mlv, Irv]"
Category="InvalidData"
}
$param1=2
Write-error #ErrorParams1
Exit
}
If ($param2 -eq 0 -and $param1 -ne 1)
{
$ErrorParams2 = #{
Message="SYNTAX ERROR: `n Check syntax: -SelectedPref [1,2,3]"
Category="InvalidData"
}
$param2=2
Write-error #ErrorParams2
Exit
}
If ($param1 + $param2 -eq 2)
{
$ErrorParams3 = #{
Message="SYNTAX ERROR: `n Specify only one parameter"
Category="InvalidData"
}
Write-error #ErrorParams3
Exit
}
}
checkParam
Related
I am trying to get info about an iSCSI-Targets Connectionstate.
Ideally I want to have a variable with a value that is True or False for connected or not connected, so that i can run commands to connect or disconnect as needed.
If i set the Variable directly it's working fine. Setting the Variable with the output of a cmdlet seems to work but comparing the Values False or True with IF is always resulting in True. For me the question is: where is the difference between setting the Variable directly to True and using the output of the cmdlet...?
Sample one:
$IsConnected=true
if($IsConnected -eq 'true') {
echo IsConnected!
} else {
echo IsNotConnected
}
Results, as expected, in: IsConnected
Sample two:
$IsConnected=False
if($IsConnected -eq 'true') {
echo IsConnected!
} else {
echo IsNotConnected
}
Results, as expected, in: IsNotConnected
So now my knowledge ends...:
Sample three:
PS ~> $IsConnected=get-iscsitarget | select -ExpandProperty IsConnected
PS ~> echo $IsConnected
True
PS ~> if($IsConnected -eq 'True') {Echo "IsConnected"}else{echo "IsNotConnected"}
IsConnected
PS ~> if($IsConnected -eq 'False') {Echo "IsConnected"}else{echo "IsNotConnected"}
IsConnected
PS ~> if($IsConnected -eq '17beerarenotenaugh') {Echo "IsConnected"}else{echo "IsNotConnected"}
IsConnected
PowerShell's comparison operators are overloaded, meaning they might have different behaviors based on the type of operand you provide.
The behavior depends entirely on the type of the left-hand side (lhs) operand you provide, and PowerShell will try to convert the right-hand side (rhs) to the same.
In your example, the value of the $IsConnected variable is a [bool] - and PowerShell therefore attempts to convert the rhs operand to a [bool] to.
The conversion logic for [string] to [bool] is:
Empty strings = $false
Non-empty strings = $true
Since 'True', 'False' and '17beerarenotenaugh' are all non-empty, the if conditions are basically interpreted as:
if($IsConnected -eq $true) { ... }
if($IsConnected -eq $true) { ... }
if($IsConnected -eq $true) { ... }
Use the automatic variables $true and $false to avoid this:
PS ~> if($IsConnected -eq $false) {Echo "IsConnected"}else{echo "IsNotConnected"}
IsNotConnected
alternatively, use the -not operator:
PS ~> if(-not $IsConnected) {Echo "IsConnected"}else{echo "IsNotConnected"}
IsNotConnected
thanks for helping!. Our NAS disconnects sometimes or just don't connect after Reboot so maybe some others find this usefull...:
$IsConnected = Get-IscsiTarget | Select-Object -ExpandProperty IsConnected
If($IsConnected -eq $false)
{
Get-IscsiTarget | Where-Object -Property NodeAddress -Like "iqn.2000-01.com.synology:nas01.Target-myiqnnumber" |
Connect-IscsiTarget -AuthenticationType ONEWAYCHAP -ChapUsername mychapuser -ChapSecret mychapuserssecret
}
else
{
echo "IsConnected=$Isconnected iSCSI-Drive:OK"
}
Please see latest code that is now working, there is no longer any need for any Invoke cmdlet:
$ClassificationList = $null
$classifications = $null
$ClassificationList = $ConfigFile.Settings.Project.Classifications
If ( $ClassificationList )
{
$ClassificationList = $ClassificationList -replace ',','|'
$classifications = $wsus.GetUpdateClassifications() |
where title -match $ClassificationList
$updatescope.Classifications.Clear()
$updatescope.Classifications.AddRange($classifications)
}
Original Question:
This question has been condensed to avoid confusion.
When executing the below code:
$ScriptText =
#"
`$classifications = `$wsus.GetUpdateClassifications() |
? {
$_.Title -eq 'Critical Updates' `
-OR `
$_.Title -eq 'Security Updates' `
-OR `
$_.Title -eq 'Definition Updates'
}
"#
$scriptBlock = [Scriptblock]::Create($ScriptText)
Invoke-Command -ScriptBlock {$scriptBlock}
Write-Host $classifications
The variable $classifications does not get populated, but executing the code without wrapping it into a script block works fine. I am trying to read from a config file all classifications I want to search WSUS for and dynamically add them to the above script, but executing that script when it is built does not appear to work, though no errors are thrown.
I would do it this way.
$wsus.GetUpdateClassifications() |
where title -match 'critical updates|security updates|definition updates'
Don't define your code as a string and then put that string in a scriptblock.
Invoke-Command -Scriptblock {$ScriptText}
If you must create a scriptblock from a string you'd do it like this:
$ScriptText = "if ( 1 -ne 2 ) {
Write-Host 'Hello'
} else {
Write-Host 'GoodBye'
}"
Invoke-Command -ScriptBlock ([Scriptblock]::Create($ScriptText))
However, normally you'd create the scriptblock as a literal, either as a variable
$scriptblock = {
if ( 1 -ne 2 ) {
Write-Host 'Hello'
} else {
Write-Host 'GoodBye'
}
}
Invoke-Command -ScriptBlock $scriptblock
or inline
Invoke-Command -ScriptBlock {
if ( 1 -ne 2 ) {
Write-Host 'Hello'
} else {
Write-Host 'GoodBye'
}
}
I'm trying to make a loop for joining a Windows domain in PowerShell with usage of $?, which should return false/true if last command returned error or not.
This is what I got:
Add-Computer -DomainName "domainname.local" -Credential "admin"
$MaxAttempts = 0
do {
if ($? -like "false" -and $MaxAttempts -lt 5) {
$MaxAttempts += 1
Write-Host "Attempt" $MaxAttempts "out of 5"
Add-Computer -DomainName "domainname.local" -Credential "admin"
} else {
Write-Host "test"
}
Problem is when I have first command outside of that do/until loop it's returning true even when I get an error.
But when I add this command to do/until loop it returns false as expected but that way im running that command twice and that's not I want.
Can someone explain me this sorcery?
The automatic variable $? contains true or false depending on whether or not the previous PowerShell statement completed successfully. The last PowerShell statement before you check the value of $? inside the loop is $MaxAttempts = 0, which changes the value of $? to true (because the assignment operation succeeded), even if the Add-Computer statement before that failed.
I'd recommend adjusting your loop to something like this:
$MaxAttempts = 0
do {
Add-Computer -DomainName "domainname.local" -Credential "admin"
$success = $?
if (-not $success) {
$MaxAttempts++
}
} until ($success -or $MaxAttempts -ge 5) {
Using the basic construct:
try
{
Do-Something
}
catch
{
Write-Output "Something threw an exception"
}
Is it possible to keep trying the Do-Something until it succeeds? Perhaps using a while loopp like this:
$Timeout = 60
$timer = [Diagnostics.Stopwatch]::StartNew()
$t = 0
while (($timer.Elapsed.TotalSeconds -lt $Timeout) -and ($t -ne 1))) {
Start-Sleep -Seconds 1
try
{
Do-Something
$t = 1
}
catch
{
Write-Output "Something threw an exception"
}
}
$timer.Stop()
Here I use a timer to make sure PowerShell doesn't run indefinitely.
It should keep trying until the try succeeds and $t = 1 is executed. However, it fails in about 2 seconds. Please help.
More specifically the Do-Something is:
(Get-Process -Name FineReader).MainWindowHandle
I'd like the code to keep trying until FineReader exists and it can get the MainWindowHandle.
You can use break keyword.
# Set the erroracton preference to stop when an error occurs,
$ErrorActionPreferenceBak = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
While($True){
try{
Do-Something
break
}
catch{
Write-Output "Something failed"
Start-Sleep -Seconds 1 # wait for a seconds before next attempt.
}
finally{
#Reset the erroracton preference
$ErrorActionPreference = $ErrorActionPreferenceBak
}
}
Your Do-Something should be called with the switch -ErrorAction Stop in order to issue a terminating exception that can be caught by try
For that, you also need to bind your function as a CmdLet. For example:
function DoSomething {
[CmdLetBinding()]
Param(
)
# Your code
}
And then call your function with the -ErrorAction Stop switch:
try {
Do-Something -ErrorAction Stop
}
If your DoSomething is not a function but rather an existing powershell CmdLet, then ... you guessed it, just call it with -ErrorAction Stop
You can learn more about the try/catch/finally in powershell here
Assume that you have a function failing in 9 out of 10 calls:
function Get-RandomFail{
$value = [Random]::new().Next(10);
if ($value -ne 5) { throw }
return $value
}
And you want to limit time window, you can use following:
function Try-Invoke{
[CmdletBinding()]
param(
$Action,
$MaxSeconds,
$Delay=1
)
$timeout = [timespan]::FromSeconds($MaxSeconds)
$start = [DateTime]::Now
do {
try {
return &$Action
} catch {
Write-Verbose "Error"
}
Start-Sleep -Seconds $Delay
} until (([DateTime]::Now - $start) -gt $timeout)
throw
}
$result = Try-Invoke {Get-RandomFail} -MaxSeconds 5 -Verbose
Get-RandomFail will be invoked until no error occurs or until time is over. You can also use Delay argument to modify sleep time after every unsuccesfull Get-RandomFail invocation.
I have the following code in my powershell script to validate user input (The first positional argument in the script):
function validatePath {
Param
(
[Parameter(Mandatory=$true)]
[ValidateScript({
If ($_ -match "^([a-z]:\\(?:[-\\w\\.\\d])*)") {
$True
} Else {
Write-Host "Please enter a valid path,$_ is not a valid path."
Write-debug $_.Exception
Break
}
})]
[string]$filePath
)
Process
{
Write-Host "The path is "$filePath
}
}
validatePath -filePath $args[0]
My problem is, currently when the validation fails, and the code goes in the Else block and hits Break, instead of stopping the entire script from continue running, it goes to the next block and everything continues and more errors come out.
Question is, how can I modify my code so that When the validation fails to match the regex, it will throw an appropriate error message and stops the entire script from running?
Set the $ErrorActionPreference variable inside the script to Stop:
$ErrorActionPreference = 'Stop'
function validatePath {
Param
(
[Parameter(Mandatory=$true)]
[ValidateScript({
If ($_ -match "^([a-z]:\\(?:[-\\w\\.\\d])*)") {
$True
} Else {
Write-Host "Please enter a valid path,$_ is not a valid path."
Write-debug $_.Exception
Break
}
})]
[string]$filePath
)
Process
{
Write-Host "The path is "$filePath
}
}
validatePath -filePath $args[0]
Do-MoreStuff # this won't execute if parameter validation fails in the previous call
See the about_Preference_Variables help file for more information
use "exit" to stop the entire script. Put any message before the "exit".