I have the following script
function dummy
{
param([string[]] myArray)
myArray | foreach {
#do something with $_
}
}
from powershell if I do the following everything is fine
. ./myscript.ps1
dummy 'val1','val2'
but i can't get this to work from jenkins with a global var I have defined
. ./myscript.ps1
dummy $env:myglobal
where $env:myglobal = 'val1','val2'
it appears to be passing the following
dummy "'val1','val2'"
and the dummy treats it as a single string instead of a string array
I'm probably oversimplifying, but basically environment variables are just strings (i.e. not arrays!).
What you'll need to do is parse the value using -split e.g.
function dummy {
Param (
[string[]]
$myArray
)
Process {
[array]$anotherArray = $myArray -split ","
$anotherArray| ForEach-Object {
#do something with $_
}
}
}
Related
Is it possible to take an arg like "this, is, a, test" and create an array in powershell?
Something like
./myScript.ps1 -myStringArray 'this, is, a, test'
And in the code something like:
param (
[string[]] $myStringArray = #()
)
foreach($item in $myStringArray) {
Write-Host $item
}
I have tried a few way but they aren't very clean and either create a sentence with or without the commas but no new line per item.
How difficult would it be to support both:
'this, is, a, test'
And
'this,is,a,test'?
You could use the -split operator with a simple regex to archive that:
param (
[string[]] $myStringArray = #()
)
$myStringArray -split ',\s?' | ForEach-Object {
Write-Host $_
}
I'm making some modules to make RESTAPI calls easier for our software (since all resources use different logic to how I can find them and how they should be called but that's a separate story). I'm making Get-, Set-, New-, and Remove- for each resource and just found that you can't pipe more than one object to modules that takes arrays from the pipeline. Here's an illustration of the problem that you can copy-paste into your environment:
function Get-MyTest {
param(
[string[]]$MyInput
)
[array]$Output = #()
$i=0
foreach($Item in $MyInput){
$i++
$Output += [pscustomobject]#{
Name = $Item
Id = $i
}
}
return $Output
}
function Get-IncorrectResults {
param(
[parameter(ValueFromPipeline)][array]$MyIncorrectInput
)
foreach ($Item in $MyIncorrectInput) {
Write-Host "Current object: $Item `n "
}
}
The Get module can return more than one object. Each object returned is a PSCustomObject, so if there are more than one returned it becomes an array of PSCustomObjects. Here's the problem:
This works:
$Results = Get-MyTest -MyInput "Test1","Test2","Test3"
Get-IncorrectResults -MyIncorrectInput $Results
This only returns the first item:
$Results = Get-MyTest -MyInput "Test1","Test2","Test3"
$Results | Get-IncorrectResults
If the Get part returns more than one object, only the first object is passed on to the Remove-MyModule. I tried changing the parameter definition from [pscustomobject[]] to [array] but it's the same result. What is the reason for dropping all but the first array item when piping but not when using it as a parameter?
The structure of an advanced PowerShell function is as follows:
function Function-Name {
param(
<# parameter definitions go here#>
)
begin {
<# this runs once when the pipeline starts #>
}
process {
<# this runs once for every input item piped to the function #>
}
end {
<# this runs once at the end of the pipeline #>
}
}
When you omit the begin/process/end blocks, PowerShell assumes your function body is really the end block - so this function definition:
function Function-Name {
param()
Do-Something
}
... is the exact same as:
function Function-Name {
param()
begin {}
process {}
end {Do-Something}
}
Notice that the process block - the only piece of the function body that repeats with new input - is assumed empty, and the Do-Something statement will not be executed until after the last input item has been received.
This is why it looks like it "drops" everything but the last input object.
Place the code that consumes the pipeline-bound variable inside the process block and it'll work:
Remove-MyModule{
param(
[parameter(ValueFromPipeline)][pscustomobject[]]$MyModules
)
process {
foreach($Module in $MyModules){
Invoke-RestMethod -Method Delete -Uri "MyURL" -Body (ConvertTo-Json $Module) -UseDefaultCredentials
}
}
}
For more information about the syntax and semantics of advanced functions, see the about_Functions_Advanced_Methods help file
I have a script that grabs a series of information from SQL. It then parses the information and passes it to a series of arrays. I want to then pass each array to a separate script.
I've seen Start-job should be able to do this but form my testing it didn't seem to work. This is what I have tried. Each Script individually works, and I am currently just using CVS's to pass the information.
Once the information is in the script I need to be able to call specific properties from each object. I did get it to just print the array as a string, but I couldn't call anything specific.
Invoke-Sqlcmd -Query $Q1 -ServerInstance $I -Database $DB | Export-Csv "$Files\Employees.csv"
$emps = Import-Csv "$Files\Employees.csv"
$newaccounts = #()
$deacaccounts = #()
$changedusers = #()
if(Test-Path -Path "$Files\Employees.csv"){
foreach ($emp in $emps) {
if ($emp.emp_num.trim() -ne $emp.EmpNum) {
$newaccounts += $emp
}
if ($emp.emp_num.trim() -eq $emp.EmpNum) {
if ($emp.fname -ne $emp.GivenName -and $emp.lname -ne $emp.SurName) {
$deacaccounts += $emp
$newaccounts += $emp
}
else ($emp.dept -ne $emp.DepartmentNumber -or $emp.job_title -ne $emp.JobTitle) {
$changedusers += $emp
}
}
}
}
Start-job -path "script" -argumentlist (,$deacaccounts)
Start-job -path "script" -argumentlist (,$changedusers)
Start-job -path "script" -argumentlist (,$newaccounts )
EDIT:
The Information passed to the scripts would be multiple lines of employee data. I need to be able to grab that info in the "Sub" scripts and perform actions based on them.
EX:
Deacaccounts =
fname
Lname
empnum
ted
kaz
1234
sam
cart
245
If you really need background jobs - it turns out that you don't - note that Start-Job doesn't have a -Path parameter; you'd have use -ScriptBlock { & "$script" } instead.
To simply invoke the script in the foreground, in sequence, use the following (script representing your .ps1 file path(s)):
& "script" $deacaccounts
& "script" $changedusers
& "script" $newaccounts
Note: &, the call operator, is only needed if the script / executable path is quoted and/or contains variable references (or subexpresions); e.g., a script with path c:\foo\bar.ps1 may be invoked without &; e.g.
c:\foo\bar.ps1 $deacaccounts
Note that your script(s) will receive a single argument each, containing an array of values.
If instead, you wanted to pass the array elements as individual (positional) arguments, you'd have to use splatting, where you use sigil # instead of $ to pass your variable (e.g.,
& "script" #deaccounts).
If you need to enumerate the arrays and pass each object individually as a parameter, use the following:
foreach ($obj in $deaccounts) { & "script" $obj }
foreach ($obj in $changedusers) { & "script" $obj }
foreach ($obj in $newaccounts) { & "script" $obj }
If each object should be splatted positionally based on its property values:
foreach ($obj in $deaccounts) {
$vals = $obj.psobject.Properties.Value
& "script" #vals
}
# ... ditto for $changeduser and $newaccounts
If each object should be splatted by property names, based on both property names and values, you need to convert each object to a hashtable first:
foreach ($obj in $deaccounts) {
$params = #{}
foreach ($prop in $obj.psobject.Properties) {
$params[$prop.Name] = $prop.Value
}
& "script" #params
}
# ... ditto for $changeduser and $newaccounts
As an aside: Incrementally extending arrays in a loop with += is inefficient, because a new array must be created behind the scenes in every iteration, because arrays are of fixed size.
In general, a much more efficient approach is to use a foreach loop as an expression and let PowerShell itself collect the outputs in an array: [array] $outputs = foreach (...) { ... } - see this answer.
In case you need to create arrays manually, e.g to create multiple ones, such as in your case, use an efficiently extensible list type, such as [System.Collections.Generic.List[object]] - see this answer.
I'm trying add to a variable and a string in an array dynamically but i'm not getting expected output.
(1) I'm getting env name
(2) Concatinating the string and variable in an array
Code is as follows.
$env = $env:COMPUTERNAME.Substring(0,2)
$servers = { $env+"server1.test.com",$env+"server2.test.com" }
$serverCount = $servers -split(",") | measure | % { $_.Count }
For ($i=0; $i -lt $serverCount; $i++)
{
$ServerName = $servers -split(',') -replace '\[\d+\]'
$server = $ServerName[$i]
Write-Host $server
}
output i'm getting as
$env+"server1.test.com"
$env+"server2.test.com"
Values are not getting concatenated properly and variable value is not getting displayed. Any help.
$servers = { $env+"server1.test.com",$env+"server2.test.com" }
This is a scriptblock, not an array. {} is like a function, you have to run it for it to do anything (such as evaluating $env).
When you force it into a string using -split(",") what you get is text representation of the source code in the scriptblock, including the variable names.
As #Olaf comments, the right way to create an array of names is
$servers = ($env + "server1.test.com"), ($env + "server2.test.com")
This might be how I'd write it:
$env = $env:COMPUTERNAME.Substring(0,2)
"server1.test.com", "server2.test.com" | foreach-object {
"$env$_" -replace '\d+'
}
I want to create a powershell script that accepts dynamic parameters and I also want to iterate through them.
eg:
I call the powershell script in the following manner.
ParametersTest.ps1 -param1 value1 -param2 value2 -param3 value3
And I should be able to access my params inside the script as follows:
for($key in DynamicParams) {
$paramValue = DynamicParams[$key];
}
Is there anyway to do this in powershell? Thanks in advance.
There is nothing built-in like that (essentially you're asking for PowerShell parameter parsing in the absence of any definition of those parameters). You can emulate it, though. With $args you can get at all arguments of the function as an array. You can then iterate that and decompose it into names and values:
$DynamicParams = #{}
switch -Regex ($args) {
'^-' {
# Parameter name
if ($name) {
$DynamicParams[$name] = $value
$name = $value = $null
}
$name = $_ -replace '^-'
}
'^[^-]' {
# Value
$value = $_
}
}
if ($name) {
$DynamicParams[$name] = $value
$name = $value = $null
}
To iterate over dynamic parameters you can either do something like you wrote
foreach ($key in $DynamicParams.Keys) {
$value = $DynamicParams[$key]
}
(note the foreach, not for, the latter of which cannot work like you wrote it) or just iterate normally over the hash table:
$DynamicParams.GetEnumerator() | ForEach-Object {
$name = $_.Key
$value = $_.Value
}