Replace parameters in JSON with given Values from .csv - json

I have a JSON-File where I want to replace several values with a placeholder.
So i made a .csv with the parameters to replace. line[0] (if existing) is the path in the file, line[1] the element, line[2] the placeholder.
journeys.legs.origin.properties.downloads;url;placeholder;
;url;placeholder;
download;url;placeholder;
;psFileName;placeholder;
;serverTime;placeholder;
;calcTime;placeholder;
now I defined the following functions, to get the file, read the csv and replace the stuff.
$storage = "D:\Service\test.json"
$parameters2replace = "D:\Service\parameters2replace.csv"
function Get-JSONProperty([object] $InputObject, [string] $Property) {
$path = $Property -split '\.'
$obj = $InputObject
$path | %{ $obj = $obj.$_ }
$obj
}
function setParameter(){
foreach ($parameter in Get-Content $parameters2replace){
$line=$parameter.split(";")
$path = $line[0]
$elementName = $line[1]
$newValue = $line[2]
replaceElement $path $elementName $newValue
}
}
function replaceElement($path, $elementName, $newValue){
ForEach($JSONPath in Get-JSONProperty $JSON $path){
if (!$line[0]){
if($JSON.$elementName){
$JSON.$elementName = $newValue
}
}
else{
if($JSON.$path.$elementName){
echo $JSON.$path.$elementName
$JSON.$path.$elementName = $newValue
}
}
}
$JSON | ConvertTo-Json -depth 32| set-content $storage
}
$JSON = Get-Content $storage -raw | ConvertFrom-Json
setParameter
My problem now is, that the following if-argument won't work with the $path variable. If i put it in hardcoded it works just fine.
if($JSON.$path.$elementName)
I hope i could make everything clear, this was my first post.

$JSON.$path makes PowerShell look for a property with the literal name 'journeys.legs.origin.properties.downloads'. You need to split the string into it's individual parts and iterate (or recurse) through the path:
function Get-JSONPropertyValue {
param(
$JSON,
[string]$Path,
[string]$Name
)
$object = $JSON
foreach($propName in $Path.Split('.')){
$object = $object.$propName
}
return $object.$Name
}
function Set-JSONPropertyValue
{
param(
$JSON,
[string]$Path,
[string]$Name,
$Value
)
$object = $JSON
foreach($propName in $Path.Split('.')){
$object = $object.$propName
}
$object.$Name = $Value
}
Now you can do:
if($value = Get-JSONPropertyValue $JSON -Path $path -Name $elementName){
echo "Old value: $value"
Set-JSONPropertyValue $JSON -Path $path -Name $elementName -Value $newValue
}

Related

How to insert bulk document into elastic using PowerShell?

I am trying to read the data from CSV file which has 2200000 records using PowerShell and storing each record in JSON file as NDJSON format.
Total document in JSON file is 2200000 (200 MB)
Sample JSON Data
{"index":{}}
{"ip-address":"1.5.0.1","is-vpn":"true","#timestamp":"2022-12-01T18:59:48.8325021+05:30"}
{"index":{}}
{"ip-address":"243.11.0.1","is-vpn":"true","#timestamp":"2022-12-01T18:59:48.8853225+05:30"}
{"index":{}}
{"ip-address":"253.11.0.1","is-vpn":"true","#timestamp":"2022-12-01T18:59:48.8853225+05:30"}
{"index":{}}
{"ip-address":"39.24.0.1","is-vpn":"true","#timestamp":"2022-12-01T18:59:48.8853225+05:30"}
{"index":{}}
{"ip-address":"163.24.0.1","is-vpn":"true","#timestamp":"2022-12-01T18:59:48.8853225+05:30"}
Code
function Get-IPDataPath
{
$dbFilePath = Get-ChildItem -Path $rootDir -Filter "IP2*.CSV" | ForEach-Object FullName | Select-Object -First 1
Write-Host "file path - $dbFilePath"
$dbFilePath # implicit output
}
function Convert-NumberToIP
{
param(
[Parameter(Mandatory=$true)][string]$number
)
[Int64] $numberInt = 0
if( [Int64]::TryParse( $number, [ref] $numberInt ) ) {
if( ($numberInt -ge 0) -and ($numberInt -le 0xFFFFFFFFl) ) {
# Convert to IP address like '192.168.23.42'
([IPAddress] $numberInt).ToString()
}
}
# In case TryParse() returns $false or the number is out of range for an IPv4 address,
# the output of this function will be empty, which converts to $false in a boolean context.
}
function Insert-Document
{
param(
[Parameter(Mandatory=$true)][string]$indexName,
[Parameter(Mandatory=$true)][object]$filePath
)
$url = "https://esdev2:9200/naveen/_doc/_bulk"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($cred))
$headers = #{
"Authorization" = "Basic $encodedCreds"
"Transfer-Encoding" = "chunked"
"Content-Type" = "application/x-ndjson"
}
$content = Get-Content -Path $filepath
$response = Invoke-WebRequest -Uri $url -Method POST -Body $content -Headers $Headers -ContentType 'application/x-ndjson'
if($response.StatusCode -eq 201)
{
Write-Host "Documents added successfully"
}
else
{
Write-Host $response
Write-Host "$($response.content)"
throw "Trouble adding document: $_"
}
}
$dbFilePath = Get-IPDataPath
$outputFile = Join-Path -Path $rootDir -ChildPath "output.json"
Write-Host "Converting CSV file $dbFilePath to $outputFile"
$object = [PSCustomObject]#{
'ip-address' = ''
'is-vpn' = 'true'
'#timestamp' = ''
}
# Enclose foreach loop in a script block to be able to pipe its output to Set-Content
$count = 0
& {
foreach( $item in [Linq.Enumerable]::Skip( [IO.File]::ReadLines( $dbFilePath ), 1 ) )
{
$row = $item -split ','
$ipNumber = $row[0].trim('"')
if( $ip = Convert-NumberToIP -number $ipNumber )
{
$index = [PSCustomObject]#{"index" = [PSCustomObject]#{}} | ConvertTo-Json -Depth 10 -Compress
$object.'ip-address' = $ip
$object.'#timestamp' = (Get-Date).ToString('o')
#$index | ConvertTo-Json -Depth 100 -Compress
$document = $object | ConvertTo-Json -Depth 100 -Compress
if($count -lt 25)
{
Add-Content -Path $outputFile $index
Add-Content -Path $outputFile $document
$count++
}
else
{
$count = 0
BulkInsert-Document -indexName $indexName -filePath $outputFile
Clear-Content -Path $outputFile
}
}
}
} | Set-Content -Path $outputFile
Write-Host "Inserting document in elastic"
Insert-Document -indexName $indexName -filePath $outputFile
But I am getting Error: {"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"The bulk
request must be terminated by a newline
[\n]"}],"type":"illegal_argument_exception","reason":"The bulk request must be
terminated by a newline [\n]"},"status":400}
Though I have new line at the end of JSON file, Could you please help how to insert all the document.

How to escape chinese in PowerShell with ConvertTo-Json?

I am trying to use ConvertTo-Json in PowerShell. By default, ConvertTo-Json will escape special characters. However, it will not escape chinese.
For example:
"<>中文ABC" | ConvertTo-Json
The output is
"\u003c\u003e中文ABC"
But, what I really want is "\u003c\u003e\u4e2d\u6587ABC". Can anyone share the experience?
Found a way:
function ConvertTo-Rtf
{
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline = $true)]
[string[]] $String
)
process
{
if ($null -eq $String) { $String = #() }
$stringBuilder = New-Object System.Text.StringBuilder
foreach ($s in $String)
{
$stringBuilder.Length = 0
foreach ($character in $s.GetEnumerator())
{
if ([int]$character -lt 0x7F)
{
$null = $stringBuilder.Append($character)
}
else
{
$null = $stringBuilder.AppendFormat('\u{0:x}', [int]$character)
}
}
$stringBuilder.ToString()
}
}
}
Then #{name="ABC<中文>" } | ConvertTo-Json -Compress| ConvertTo-Rtf
Output:
{"name":"ABC\u003c\u4e2d\u6587\u003e"}

powershell convert json to hashtable recursively [duplicate]

What is the easiest way to convert a PSCustomObject to a Hashtable? It displays just like one with the splat operator, curly braces and what appear to be key value pairs. When I try to cast it to [Hashtable] it doesn't work. I also tried .toString() and the assigned variable says its a string but displays nothing - any ideas?
Shouldn't be too hard. Something like this should do the trick:
# Create a PSCustomObject (ironically using a hashtable)
$ht1 = #{ A = 'a'; B = 'b'; DateTime = Get-Date }
$theObject = new-object psobject -Property $ht1
# Convert the PSCustomObject back to a hashtable
$ht2 = #{}
$theObject.psobject.properties | Foreach { $ht2[$_.Name] = $_.Value }
Keith already gave you the answer, this is just another way of doing the same with a one-liner:
$psobject.psobject.properties | foreach -begin {$h=#{}} -process {$h."$($_.Name)" = $_.Value} -end {$h}
Here's a version that works with nested hashtables / arrays as well (which is useful if you're trying to do this with DSC ConfigurationData):
function ConvertPSObjectToHashtable
{
param (
[Parameter(ValueFromPipeline)]
$InputObject
)
process
{
if ($null -eq $InputObject) { return $null }
if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string])
{
$collection = #(
foreach ($object in $InputObject) { ConvertPSObjectToHashtable $object }
)
Write-Output -NoEnumerate $collection
}
elseif ($InputObject -is [psobject])
{
$hash = #{}
foreach ($property in $InputObject.PSObject.Properties)
{
$hash[$property.Name] = ConvertPSObjectToHashtable $property.Value
}
$hash
}
else
{
$InputObject
}
}
}
My extremely lazy approach, enabled by a new feature in PowerShell 6:
$myhashtable = $mypscustomobject | ConvertTo-Json | ConvertFrom-Json -AsHashTable
This works for PSCustomObjects created by ConvertFrom_Json.
Function ConvertConvertFrom-JsonPSCustomObjectToHash($obj)
{
$hash = #{}
$obj | Get-Member -MemberType Properties | SELECT -exp "Name" | % {
$hash[$_] = ($obj | SELECT -exp $_)
}
$hash
}
Disclaimer: I barely understand PowerShell so this is probably not as clean as it could be. But it works (for one level only).
My code:
function PSCustomObjectConvertToHashtable() {
param(
[Parameter(ValueFromPipeline)]
$object
)
if ( $object -eq $null ) { return $null }
if ( $object -is [psobject] ) {
$result = #{}
$items = $object | Get-Member -MemberType NoteProperty
foreach( $item in $items ) {
$key = $item.Name
$value = PSCustomObjectConvertToHashtable -object $object.$key
$result.Add($key, $value)
}
return $result
} elseif ($object -is [array]) {
$result = [object[]]::new($object.Count)
for ($i = 0; $i -lt $object.Count; $i++) {
$result[$i] = (PSCustomObjectConvertToHashtable -object $object[$i])
}
return ,$result
} else {
return $object
}
}
For simple [PSCustomObject] to [Hashtable] conversion Keith's Answer works best.
However if you need more options you can use
function ConvertTo-Hashtable {
<#
.Synopsis
Converts an object to a hashtable
.DESCRIPTION
PowerShell v4 seems to have trouble casting some objects to Hashtable.
This function is a workaround to convert PS Objects to [Hashtable]
.LINK
https://github.com/alainQtec/.files/blob/main/src/scripts/Converters/ConvertTo-Hashtable.ps1
.NOTES
Base ref: https://community.idera.com/database-tools/powershell/powertips/b/tips/posts/turning-objects-into-hash-tables-2
#>
PARAM(
# The object to convert to a hashtable
[Parameter(ValueFromPipeline = $true, Mandatory = $true)]
$InputObject,
# Forces the values to be strings and converts them by running them through Out-String
[switch]$AsString,
# If set, empty properties are Included
[switch]$AllowNulls,
# Make each hashtable to have it's own set of properties, otherwise,
# (default) each InputObject is normalized to the properties on the first object in the pipeline
[switch]$DontNormalize
)
BEGIN {
$headers = #()
}
PROCESS {
if (!$headers -or $DontNormalize) {
$headers = $InputObject | Get-Member -type Properties | Select-Object -expand name
}
$OutputHash = #{}
if ($AsString) {
foreach ($col in $headers) {
if ($AllowNulls -or ($InputObject.$col -is [bool] -or ($InputObject.$col))) {
$OutputHash.$col = $InputObject.$col | Out-String -Width 9999 | ForEach-Object { $_.Trim() }
}
}
} else {
foreach ($col in $headers) {
if ($AllowNulls -or ($InputObject.$col -is [bool] -or ($InputObject.$col))) {
$OutputHash.$col = $InputObject.$col
}
}
}
}
END {
return $OutputHash
}
}
Maybe this is overkill but I hope it Helps
Today, the "easiest way" to convert PSCustomObject to Hashtable would be so:
$custom_obj | ConvertTo-HashtableFromPsCustomObject
OR
[hashtable]$custom_obj
Conversely, you can convert a Hashtable to PSCustomObject using:
[PSCustomObject]$hash_table
Only snag is, these nifty options may not be available in older versions of PS

Create editable powershell GUI for CSV data

GOAL: Create a GUI form populated with CSV data, allow the user to edit the data, then save the data in an array for further manipulation.
NOTE: Using PowerShell Studio to generate a form with data from the CSV
CURRENT CODE:
- Calling code ($path is passed from the calling form):
$rows = Import-Csv -Path $path
$table = ConvertTo-DataTable -InputObject $rows
Load-DataGridView -DataGridView $datagridviewResults -Item $table
ConvertTo-DataTable function:
function ConvertTo-DataTable {
[OutputType([System.Data.DataTable])]
param(
[ValidateNotNull()]
$InputObject,
[ValidateNotNull()]
[System.Data.DataTable]$Table,
[switch]$RetainColumns,
[switch]$FilterWMIProperties
)
if($Table -eq $null) {
$Table = New-Object System.Data.DataTable
}
if($InputObject-is [System.Data.DataTable]) {
$Table = $InputObject
} else {
if(-not $RetainColumns -or $Table.Columns.Count -eq 0) {
#Clear out the Table Contents
$Table.Clear()
if($InputObject -eq $null){ return } #Empty Data
$object = $null
#find the first non null value
foreach($item in $InputObject) {
if($item -ne $null) {
$object = $item
break
}
}
if($object -eq $null) { return } #All null then empty
#Get all the properties in order to create the columns
foreach ($prop in $object.PSObject.Get_Properties()) {
if(-not $FilterWMIProperties -or -not $prop.Name.StartsWith('__')) { #filter out WMI properties
#Get the type from the Definition string
$type = $null
if($prop.Value -ne $null) {
try{ $type = $prop.Value.GetType() } catch {}
}
if($type -ne $null) { # -and [System.Type]::GetTypeCode($type) -ne 'Object')
[void]$table.Columns.Add($prop.Name, $type)
} else { #Type info not found
[void]$table.Columns.Add($prop.Name)
}
}
}
if($object -is [System.Data.DataRow]) {
foreach($item in $InputObject) {
$Table.Rows.Add($item)
}
return #(,$Table)
}
} else {
$Table.Rows.Clear()
}
foreach($item in $InputObject) {
$row = $table.NewRow()
if($item) {
foreach ($prop in $item.PSObject.Get_Properties()) {
if($table.Columns.Contains($prop.Name)) {
$row.Item($prop.Name) = $prop.Value
}
}
}
[void]$table.Rows.Add($row)
}
}
return #(,$Table)
}
Load-DataGridView function:
function Load-DataGridView {
Param (
[ValidateNotNull()]
[Parameter(Mandatory=$true)]
[System.Windows.Forms.DataGridView]$DataGridView,
[ValidateNotNull()]
[Parameter(Mandatory=$true)]
$Item,
[Parameter(Mandatory=$false)]
[string]$DataMember
)
$DataGridView.SuspendLayout()
$DataGridView.DataMember = $DataMember
$DataGridView.EditMode = 'EditOnEnter'
if ($Item -is [System.ComponentModel.IListSource]`
-or $Item -is [System.ComponentModel.IBindingList]`
-or $Item -is [System.ComponentModel.IBindingListView]) {
$DataGridView.DataSource = $Item
} else {
$array = New-Object System.Collections.ArrayList
if ($Item -is [System.Collections.IList]) {
$array.AddRange($Item)
} else {
$array.Add($Item)
}
$DataGridView.DataSource = $array
}
$DataGridView.ResumeLayout()
}
ADDITIONAL INFORMATION: Code is working in that it generates the Grid View and populates it with CSV data. However, I cannot edit it and need help coding the ability to capture changes once it is edited.
Thanks in advance.
12/5 EDIT: Added "$DataGridView.EditMode = 'EditOnEnter'" to the function "Load-DataGridView" above. Nothing changed. Tried to invoke the "BeginEdit" Event in a new RowCellClick event, but that didn't work either. Still struggling with this one.
For anyone else you has struggled with this ....
set-strictmode -Version 2.0
function EditCSV($title, $Instructions, $csvPath, $x = 100, $y=100, $Width=600, $Height=400, $SaveChangesToFile=$true, $ReturnStatusOrArray='Status') {
#Windows Assemblies
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
#LoadCSV
#Variables MUST have script scope to allow form to see them
$script:Updated = $false
$script:CsvData = New-Object System.Collections.ArrayList
$script:CsvData.AddRange((import-csv $csvPath))
#Helper Functions
function paint($form, $ctrl, $TablIndex, $name, $Text, $x, $y, $Width, $Height){
try{$form.Controls.Add($ctrl) }catch{}
try{$ctrl.TabIndex = $TablIndex }catch{}
try{$ctrl.Text = $Text }catch{}
try{$ctrl.name = $name }catch{}
try{$ctrl.Location = System_Drawing_Point $x $y }catch{}
try{$ctrl.size = System_Drawing_Size $Width $Height }catch{}
try{$ctrl.DataBindings.DefaultDataSourceUpdateMode = 0 }catch{}
$ctrl
}
function System_Drawing_Point($x, $Y) {$_ = New-Object System.Drawing.Point; $_.x = $X; $_.Y = $Y; $_}
function System_Drawing_Size( $Width, $Height){$_ = New-Object System.Drawing.Size; $_.Width = $Width; $_.Height = $Height; $_}
#Paint Form
$form1 = paint $null (New-Object System.Windows.Forms.Form) $null 'form1' $Title $x $y $Width $Height
$form1.add_Load({
$dataGrid1.DataSource = $script:CsvData;
$form1.refresh()
})
$label1 = paint $form1 (New-Object System.Windows.Forms.Label) $null "label1" "$Instructions" 12 13 ($width-100) 23
$label1.Font = New-Object System.Drawing.Font("Microsoft Sans Serif",9.75,2,3,0)
$label1.ForeColor = [System.Drawing.Color]::FromArgb(255,0,102,204)
$buttonSave = paint $form1 (New-Object System.Windows.Forms.Button) 1 "button1" "Save" ($width-200) ($Height-75) 75 23
$buttonSave.UseVisualStyleBackColor = $True
$buttonSave.add_Click({
$script:Updated = $true
$Form1.Close()
})
$buttonClose = paint $form1 (New-Object System.Windows.Forms.Button) 2 'button2' 'Close' ($width-105) ($Height-75) 75 23
$buttonClose.UseVisualStyleBackColor = $True
$buttonClose.add_Click({
$Form1.Close()
})
$dataGrid1 = paint $form1 (New-Object System.Windows.Forms.DataGrid) 0 "dataGrid0" $Null 12 40 ($width-40) ($Height-125)
$dataGrid1.HeaderForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0)
#Show and Wait till complete
$form1.ShowDialog()| Out-Null
#Save CSV
if( $SaveChangesToFile -eq $true -and $script:Updated ){
$script:CsvData| export-csv -NoTypeInformation -path $csvPath
}
if( $ReturnStatusOrArray -eq 'Status'){
return $script:Updated
}else{
return $script:CsvData
}
}
## Unit Test
cls
function script:Indent-ConsoleOutput($output, $indent = ' '){
if(!($output -eq $null)){
if(!( $indent -is [string])){
$indent = ''.PadRight($indent)
}
$width = (Get-Host).UI.RawUI.BufferSize.Width - $indent.length
($output| out-string).trim().replace( "`r", "").split("`n").trimend()| %{
for($i=0; $i -le $_.length; $i+=$width){
if(($i+$width) -le $_.length){
"$indent"+$_.substring($i, $width)
}else{
"$indent"+$_.substring($i, $_.length - $i)
}
}
}
}
}
Write-Host '## Before '.PadRight(120, '#')
$filePath = 'C:\temp\Text.csv'
$d = (dir c: |select-object -property Directory, Mode, LastWriteTime, Length, Name)[0..5]
$d |export-csv -path $filePath -NoTypeInformation
Indent-ConsoleOutput (import-csv $filePath |format-table) 4
Write-Host '## Edit - Save to File '.PadRight(120, '#')
Indent-ConsoleOutput (EditCSV 'Example of PS Editable Grid' '[SAVE] - To Save Changes' $filePath ) 4
Write-Host '## After '.PadRight(120, '#')
Indent-ConsoleOutput (import-csv $filePath |format-table) 4
Write-Host '## Edit - Return Array '.PadRight(120, '#')
Indent-ConsoleOutput (EditCSV 'Example of PS Editable Grid' '[SAVE] - To Save Changes' $filePath -SaveChangesToFile $false -ReturnStatusOrArray 'Array'|format-table) 4
Indent-ConsoleOutput (import-csv $filePath |format-table) 4
Enjoy

Powershell Recursion with Return

I am trying to write a recursive function that will return information in an array, however when I put a return statement into the function it misses certain entries.
I am trying to recursively look through a specified depth of folders getting the acl's associated with the folder. I know getChildItem has a recurse option, but I only want to step through 3 levels of folders.
The excerpt of code below is what I have been using for testing. When getACLS is called without a return statement (commented out below) the results are:
Folder 1
Folder 12
Folder 13
Folder 2
When the return statement is used I get the following output:
Folder 1
Folder 12
So it looks like the return statement is exiting out from the recursive loop?
The idea is that I want to return a multidimensional array like [folder name, [acls], [[subfolder, [permissions],[[...]]]]] etc.
cls
function getACLS ([string]$path, [int]$max, [int]$current) {
$dirs = Get-ChildItem -Path $path | Where { $_.psIsContainer }
$acls = Get-Acl -Path $path
$security = #()
foreach ($acl in $acls.Access) {
$security += ($acl.IdentityReference, $acl.FileSystemRights)
}
if ($current -le $max) {
if ($dirs) {
foreach ($dir in $dirs) {
$newPath = $path + '\' + $dir.Name
Write-Host $dir.Name
# return ($newPath, $security, getACLS $newPath $max ($current+1))
# getACLS $newPath $max ($current+1)
return getACLS $newPath $max ($current+1)
}
}
} elseif ($current -eq $max ) {
Write-Host max
return ($path, $security)
}
}
$results = getACLS "PATH\Testing" 2 0
The problem was the location of the return. I had it inside the foreach loop, meaning it was trying to return multiple times in the one function. I moved it outside the foreach, into the if statement instead.
function getACLS ([string]$path, [int]$max, [int]$current) {
$dirs = Get-ChildItem -Path $path | Where { $_.psIsContainer }
$acls = Get-Acl -Path $path
$security = #()
$results = #()
foreach ($acl in $acls.Access) {
$security += ($acl.IdentityReference, $acl.FileSystemRights)
}
if ($current -lt $max) {
if ($dirs) {
foreach ($dir in $dirs) {
$newPath = $path + '\' + $dir.Name
$next = $current + 1
$results += (getACLS $newPath $max $next)
}
} else {
$results = ($path, $security)
}
return ($path, $security, $results)
} elseif ($current -eq $max ) {
return ($path, $security)
}
}
In recursion, I would only use a return statement where I needed to end the recursion - just for clarity. I've done a good bit of recursion in PowerShell and it works well. However you need to remember that PowerShell functions do behave differently. The following:
return 1,2
is equivalent to:
1,2
return
In other words, anything you don't capture in a variable or redirect to a file (or $null) is automatically considered output of the function. Here's a simple example of a working, recursive function:
function recursive($path, $max, $level = 1)
{
$path = (Resolve-Path $path).ProviderPath
Write-Host "$path - $max - $level"
foreach ($item in #(Get-ChildItem $path))
{
if ($level -eq $max) { return }
recursive $item.PSPath $max ($level + 1)
}
}
recursive ~ 3
Update: I am leaving the first answer as is and adding the new code here. I see that there are multiple issues with your code. here is the updated one.
cls
function getACLS ([string]$path, [int]$max, [int]$current) {
$dirs = Get-ChildItem -Path $path | Where { $_.psIsContainer }
$acls = Get-Acl -Path $path
$security = #()
foreach ($acl in $acls.Access) {
$security += ($acl.IdentityReference, $acl.FileSystemRights)
}
if ($current -lt $max) {
if ($dirs) {
foreach ($dir in $dirs) {
$newPath = $dir.FullName
$security
getACLS $newPath $max ($current+1)
}
}
} elseif ($current -eq $max ) {
Write-Host max
return $security
}
}
$results = getACLS "C:\Scripts" 2 0
If you see above, I am not using return. I just throw the object from the GetACLs function. Also, I modified it to return on $security for testing purpose. I can see the all ACLs in $results. I changed the first if condition to if ($current -lt $max). It should not be if ($current -le $max).
Let me know if this what you are looking for. I can continue to optimize this.
==========================================OLD=============================================
Return will exit the function.
I am not providing the complete solution here but want to give you an idea about how this can be changed.
You can use PS Custom object to capture the information you need. For example,
function GetItem {
$itemsArray = #()
Get-ChildItem C:\Scripts | ForEach-Object {
$itemsObject = New-Object PSObject
Add-Member -InputObject $itemsObject -MemberType NoteProperty -Name "FullName" -Value $_.FullName
Add-Member -InputObject $itemsObject -MemberType NoteProperty -Name "Name" -Value $_.Name
$itemsArray += $itemsObject
}
return $itemsArray
}
This way you can return the object once it is completely built with the information you need.
I found none of these solutions did what I need to, which was to have an array with the results of the whole recursive function. Since we can't initialise the array inside the function, otherwise it is re-initialised every time recursion is used, we have to define it outside and use global:
# Get folder contents recursively and add to an array
function recursive($path, $max, $level = 1)
{
Write-Host "$path" -ForegroundColor Red
$global:arr += $path
foreach ($item in #(Get-ChildItem $path))
{
if ($level -eq $max) { return }
if ($item.Length -eq "1") # if it is a folder
{
$newpath = "$path\$($item.Name)"
recursive $newpath $max ($level + 1)
}
else { # else it is a file
Write-Host "$path\$($item.Name)" -ForegroundColor Blue
$global:arr +="$path\$($item.Name)"
}
}
}
$arr = #() # have to define this outside the function and make it global
recursive C:\temp\test2 4
write-host $arr.count