I have a script that uses multiple arguments, some of which contain spaces. The script is called from another script, so I pass the arguments to it from variables with the calling script.
Calling script:
$script = "C:\Path\script.ps1"
$arg1 = "SomeValue"
$arg2 = "1234"
$arg3 = #("Value1","Some Value","Value 2")
$arg4 = $true
Invoke-Command $script -Arg1 $arg1 -Arg2 $arg2 -Arg3 $arg3 -Arg4 $arg4
The called script looks like this:
param (
[Parameter(Mandatory=$false,Position=0)]
[String]$arg1,
[Parameter(Mandatory=$false,Position=1)]
[String]$arg2,
[Parameter(Mandatory=$false,Position=2)]
[array]$arg3,
[Parameter(Mandatory=$false,Position=3)]
[bool]$arg4
)
# Do stuff with the arguments
When I call the script, I get the following error:
"A positional parameter cannot be found that accepts argument 'Some'."
I've also manually called the script (bypassing the calling script) in a powershell window as below:
powershell.exe -ExecutionPolicy bypass C:\Path\script.ps1 -Arg1 "SomeValue" -Arg2 "1234" -Arg3 #("Value1","Some Value","Value 2") -Arg4 $true
powershell.exe -ExecutionPolicy bypass C:\Path\script.ps1 -Arg1 "SomeValue" -Arg2 "1234" -Arg3 "Value1","Some Value","Value 2" -Arg4 $true
powershell.exe -ExecutionPolicy bypass C:\Path\script.ps1 -Arg1 "SomeValue" -Arg2 "1234" -Arg3 "Value1","SomeValue","Value2" -Arg4 $true
powershell.exe -ExecutionPolicy bypass C:\Path\script.ps1 -Arg1 "SomeValue" -Arg2 "1234" -Arg3 "Value1,SomeValue,Value2" -Arg4 $true
None of these variations work. I've also tried the ideas found here by changing the Arg3 value to (,$args) but that doesn't work. I also changed the parameter type as found here, but that didn't work either.
The goal is to be able to pass multiple variables (some with spaces) to the script through an argument/parameter.
EDIT 12/22/16: The goal includes passing this same information from a shortcut/typed command. For example, my calling script creates a RunOnce entry in the registry to reference the called script and places the arguments in the call just like the manual examples above. None of them work, either.
Set-ItemProperty $RegROPath "(Default)" -Value "powershell.exe -ExecutionPolicy Bypass $scriptPath $argumentList" -type String
Replace Invoke-Command With & or .
Use & if all you want is output, . if you want it to run in the current context (e.g. retain all variables set)
Get-Help about_Scripts for more detail (or read online version here)
Edit: Forgot to mention, it's not your script that is throwing that error, it's Invoke-Command. If you absolutely have to use Invoke-Command you need (running remotely for example) to pass the arguments as the parameter ArgumentList like this:
$script = "C:\Path\script.ps1"
$argumentList = #(
'-arg1 "SomeValue"',
'-arg2 1234',
'-arg3 #("Value1","Some Value","Value 2")',
'-arg4 $true'
)
Invoke-Command -FilePath $script -ArgumentList $argumentList
Edit 2:
I will try your suggestion as soon as I am able to. One question, what if I need to add a conditional argument? Currently, I add arguments to the list with $argumentlist += ("arg5", "value"). Some of them are conditional: if ($bool) {$argumentlist += ("arg5", "value")}. Is there a way to do that in your example?
Yes you can, the $argumentList variable in the example is an array like any other. It can be defined all at once, defined empty and added to later, or any mix.
Example
$argumentList = #(
'-arg1 "SomeValue"',
'-arg2 1234',
'-arg3 #("Value1","Some Value","Value 2")',
'-arg4 $true'
)
if ($bool) {
$argumentList += '-arg5 "value"'
}
Invoke-Command -FilePath $script -ArgumentList $argumentList
But again, unless you are running the command on a remote computer or PSSession, you should use & or dot sourcing (.). You can still add arguments conditionally using splatting (about_Splatting)
Example
$scriptParamsSplat = #{
arg1 = "SomeValue"
arg2 = 1234
arg3 = #("Value1","Some Value","Value 2")
arg4 = $true
}
if ($bool) {
$scriptParamsSplat.arg5 = "value"
}
& 'C:\Path\To\script.ps1' #scriptParamsSplat
Related
I want to launch a admin powershell with two commands :
$command1 = "Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{example}' -Name DHCPClassId -Value National"
$command2 = "Restart-NetAdapter -Name Ethernet"
Start-Process powershell -Verb 'RunAs' -ArgumentList "-NoExit","-Command &{$command1,$command2}"
And i have an error : he don't want to execute these two because they have a same parameter (-Name), so, what can i do ?
PowerShell's statement separator is ;, not , (the latter constructs arrays).
Also:
Even though passing arguments individually, as elements of an array to Start-Process is conceptually clean, it should be avoided due to a long-standing bug: pass a single string encoding all arguments to -ArgumentList - see this answer for more information.
When using PowerShell's CLI, -Command & { ... } is never necessary - just use -Command ...; that is, there is no reason to construct a script block ({ ... }) that you must then invoke with & - all commands given are directly executed by default.
To put it all together:
$command1 = "Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\{example}' -Name DHCPClassId -Value National"
$command2 = "Restart-NetAdapter -Name Ethernet"
Start-Process powershell -Verb RunAs "-NoExit -Command $command1; $command2"
Test.ps1
Param (
[String]$CountryCode,
[String]$FilesPath,
[String]$KeepassDatabase,
[String]$KeepassKeyFile,
[String]$EventLog,
[String]$EventSource
)
Write-Host 'Ok' -ForegroundColor Yellow
Write-Host $PSBoundParameters
Start-Sleep -Seconds 5
The goal is to call the script with named parameters in elevated mode. When using named parameters without $Credential, it works fine. The window pops up and the word Ok is displayed:
$StartParams = #{
ArgumentList = "-File `"Test.ps1`" -verb `"runas`" -FilesPath `"S:\Files`" -CountryCode `"XXX`""
}
Start-Process powershell #StartParams
When I add the Credential argument it also pops-up but I can't see anything:
$StartParams = #{
Credential = Get-Credential
ArgumentList = "-File `"Test.ps1`" -verb `"runas`" -FilesPath `"S:\Files`" -CountryCode `"XXX`""
}
Start-Process powershell #StartParams
Am I missing something super obvious here? Even when using the same credentials as the logged on user, I can't see the text.
You need to specify an absolute path to the file. The new PowerShell-process (which will run as admin) doesn't run in the same working directory as your current session.
Try:
$StartParams = #{
FilePath = "powershell.exe"
Credential = Get-Credential
Verb = "RunAs"
ArgumentList = "-File `"c:\temp\Test.ps1`" -FilesPath `"S:\Files`" -CountryCode `"XXX`""
}
Start-Process #StartParams
If you only know the relative path, use Resolve-Path to convert it. Ex:
ArgumentList = "-NoExit -File `"$(Resolve-Path test.ps1 | Select-Object -ExpandProperty Path)`" -FilesPath `"S:\Files`" -CountryCode `"XXX`""
You should also look into string format or here-string so you can avoid escaping every double quote. It makes your life easier:
#Using here-string (no need to escape double quotes)
ArgumentList = #"
-NoExit -File "$(Resolve-Path test.ps1 | Select-Object -ExpandProperty Path)" -FilesPath "S:\Files" -CountryCode "XXX"
"#
#Using string format
ArgumentList = '-NoExit -File "{0}" -FilesPath "{1}" -CountryCode "{2}"' -f (Resolve-Path test.ps1 | Select-Object -ExpandProperty Path), "S:\Files", "XXX"
ScriptToInvoke.ps1:
[CmdletBinding()]
param (
[Parameter(Mandatory=$True)]
[string[]]$StringArray,
[ValidateSet('Mode1', 'Mode2', 'Mode3')]
[string]$Mode = 'Mode1'
)
$count = $StringArray.Count
Write-Verbose ("String array count ($count): $StringArray")
ScriptCallingStartProcess.ps1:
$stringArray = #('String1','String2','String3')
Start-Process powershell -Verb RunAs
-ArgumentList "-NoExit -File ScriptToInvoke.ps1 -StringArray ""$stringArray"" -Mode Mode3 -Verbose"
In this case the $stringArray is treated as an array containing one element:
screen capture of script run output
I have tried multiple variations passing the $stringArray argument:
-StringArray $stringArray
-StringArray #($stringArray)
-StringArray #(,#($stringArray))
each with the same error:
A positional parameter cannot be found that accepts argument 'String2'
As I understand it the double quotes around the ArgumentList value result in any variable being parsed. Is it possible to prevent this from happening? Or is there an alternative approach?
My use case involves attempting to re-run the Powershell script with elevated permissions to uninstall a Windows update, which is why I use Start-Process with -Verb RunAs.
Here's one way. ScriptCallingStartProcess.ps1:
$stringArray = #('String1','String2','String3')
$OFS=","
Start-Process powershell.exe -Verb Runas "-NoExit -File \ScriptToInvoke.ps1 $stringArray -Mode Mode3 -Verbose"
ScriptToInvoke.ps1:
[CmdletBinding()]
param (
[Parameter(Mandatory=$True)]
[String] $InputData,
[ValidateSet('Mode1', 'Mode2', 'Mode3')]
[string]$Mode = 'Mode1'
)
$StringArray = $InputData -split ','
$count = $StringArray.Count
Write-Verbose ("String array count ($count): $StringArray")
(that is, pass a comma-delimited string as first parameter, then split inside second script)
I escaped the double quotes, and it seems to work ok:
Start-Process powershell -Verb RunAs -ArgumentList "-NoExit -File test.ps1 -StringArray `"$stringArray`" -Mode Mode3 -Verbose"
How do you call a PowerShell script which takes named arguments from within a PowerShell script?
foo.ps1:
param(
[Parameter(Mandatory=$true)][String]$a='',
[Parameter(Mandatory=$true)][ValidateSet(0,1)][int]$b,
[Parameter(Mandatory=$false)][String]$c=''
)
#stuff done with params here
bar.ps1
#some processing
$ScriptPath = Split-Path $MyInvocation.InvocationName
$args = "-a 'arg1' -b 2"
$cmd = "$ScriptPath\foo.ps1"
Invoke-Expression $cmd $args
Error:
Invoke-Expression : A positional parameter cannot be found that accepts
argument '-a MSFT_VirtualDisk (ObjectId =
"{1}\\YELLOWSERVER8\root/Microsoft/Windo...).FriendlyName -b 2'
This is my latest attempt - I've tried multiple methods from googling none seem to work.
If I run foo.ps1 from the shell terminal as ./foo.ps1 -a 'arg1' -b 2 it works as expected.
After posting the question I stumbled upon the answer. For completeness here it is:
bar.ps1:
#some processing
$ScriptPath = Split-Path $MyInvocation.InvocationName
$args = #()
$args += ("-a", "arg1")
$args += ("-b", 2)
$cmd = "$ScriptPath\foo.ps1"
Invoke-Expression "$cmd $args"
Here is something that might help future readers:
foo.ps1:
param ($Arg1, $Arg2)
Make sure to place the "param" code at the top before any executable code.
bar.ps1:
& "path to foo\foo.ps1" -Arg1 "ValueA" -Arg2 "ValueB"
That's it !
How to pass Multiple Variables as Arguments to a Script using Start-job.
Start-Job -Name "$jobName" -filepath $TestTool -ArgumentList $compare1
how to retrieve this argument values (of $arg1 and $arg2) in a script TestTool.ps1?
Rgds
Naveen
PS>Start-Job -Name test -ArgumentList #("hello","word") -FilePath \\server\share\test.ps1
in test.ps1 just echo the $args var
$args
result :
PS>Receive-Job test -keep
hello
word
http://technet.microsoft.com/en-us/library/hh849698.aspx
"Because all of the values that follow the ArgumentList parameter name are interpreted as being values of ArgumentList, the ArgumentList parameter should be the last parameter in the command."
So I guess tha something like:
... -ArgumentList $arg1 $arg2
should work?