ForEach loop is leaving my variable empty - powershell

Complete novice to Powershell so, my apologies ahead of time for the likely easy task for most in this group.
$File = Get-ChildItem "C:\location"
$ACLs= foreach ($item in $File) {get-acl $item.FullName}
$foldernames = $acls.pschildname
$ACLNames = $acls.Access.IdentityReference
$ACLNamesNew = foreach ($ACLName in $ACLNames) {
$ACLNameString = $ACLname.Value.ToString()
$ACLNameFormatted = $ACLNameString.Split("\")[0]
}
Ultimate goal is to take the string, trim just the domain/group names from the string, pull only the unique values, and write whats left to the variable $ACLNames. Haven't even tried filtering unique as even the above leaves $ACLNamesNew empty. The foreach seams sound from my testing, but it doesn't write the values into the variable as I expected.

If you want to collect the result of a loop you have to output something inside your loop. This should be enough .. if I've got you right
$fileList = Get-ChildItem 'C:\location'
$ACLList = foreach ($file in $fileList) { get-acl $file.FullName }
$ACLNameList = $ACLList.Access.IdentityReference
$ACLNameNewList = foreach ($ACLName in $ACLNameList) {
$ACLname.Value.ToString().Split("\")[0]
}
$ACLNameNewList

Related

Outputs and easy algorithm question Object Oriented

$a = dir
foreach ($file in $a)
{
if (($file.index%2 ) -eq 0)
//Hopefully this function works, supposed to
(Ideally) print every other file
{
Write-Host $file.name
}
}
The function -eq 0... not sure if that prints out every other file. I do not know exactly how the files are numbered, or how you reference a number to the file. Do you treat every file as an object and number them? Then make a function regarding the numbers made appended to the file?
Fairly new to this, I'm used to html, css.
If you have a more proficient answer, I'm open to the idea too.
Your script almost works.
Removed alias for dir, and sorted results as requested.
The -File switch for Get-ChildItem excludes folders. I guess that's what you want, but remove it otherwise.
Since there's not an easy way to get the current position in foreach, I used a for loop instead, but it's the same idea. If you want to try with foreach, you could set a variable to true, and then not (!) it each iteration.
$Path = 'C:\yourpath'
$Files = Get-ChildItem -Path $Path -File |
Sort-Object -Property 'Name' -Descending
for ($i = 0; $i -lt $Files.Count; $i++) {
if ($i % 2 -eq 0) {
Write-Host $Files[$i].Name
}
}
If you're using this output further, it's highly recommended to write results to an object rather than the console window.
Why not simply use a for loop and increment the index counter with a value of 2?
for ($i = 0; $i -lt $a.Count; $i += 2) {
Write-Host $a[$i].Name
}

Powershell - ForEach statement - concatenate results

I'm hoping to get some help from anyone here regarding powershell scripting.
I'm trying to see if there's a way to call all the results of the ForEach statement:
ForEach ($file in $test) {
$filepath = $path+"\"+$file
write-host $filepath
}
the write-host $filepath inside the ForEach statement returns the following:
c:\....\file1.txt
c:\....\file2.txt
c:\....\file3.txt
etc...
i'm trying to see if i can get all those results and put them into 1 line that i can use outside of the foreach statement. sort of like:
c:\....\file1.txt, c:\....\file2.txt, c:\....\file3.txt etc
right now, if i use write-host $filepath outside of the ForEach statement, it only gives me the last result that $filepath got.
hope i made sense.
thank you in advance.
Nothing easier than that ... ;-)
$FullPathList = ForEach ($file in $test) {
Join-Path -Path $path -ChildPath $file
}
$FullPathList -join ','
First you create an array with the full paths, then you join them with the -join statement. ;-)
Another variant,
$path = $pwd.Path # change as needed
$test = gci # change as needed
#(ForEach ($file in $test) {
$path + "\" + $file
}) -join ", "
You might also want to get a look at the FullName property of Get-ChildItem.
If you do (gci).FullName (or maybe gci | select FullName) you'll directly get the full path.
So if $test is a gci from C:\some\dir, then $test.FullName is the array you are looking for.

Why Isn't This Counting Correctly | PowerShell

Right now, I have a CSV file which contains 3,800+ records. This file contains a list of server names, followed by an abbreviation stating if the server is a Windows server, Linux server, etc. The file also contains comments or documentation, where each line starts with "#", stating it is a comment. What I have so far is as follows.
$file = Get-Content .\allsystems.csv
$arraysplit = #()
$arrayfinal = #()
[int]$windows = 0
foreach ($thing in $file){
if ($thing.StartsWith("#")) {
continue
}
else {
$arraysplit = $thing.Split(":")
$arrayfinal = #($arraysplit[0], $arraysplit[1])
}
}
foreach ($item in $arrayfinal){
if ($item[1] -contains 'NT'){
$windows++
}
else {
continue
}
}
$windows
The goal of this script is to count the total number of Windows servers. My issue is that the first "foreach" block works fine, but the second one results in "$Windows" being 0. I'm honestly not sure why this isn't working. Two example lines of data are as follows:
example:LNX
example2:NT
if the goal is to count the windows servers, why do you need the array?
can't you just say something like
foreach ($thing in $file)
{
if ($thing -notmatch "^#" -and $thing -match "NT") { $windows++ }
}
$arrayfinal = #($arraysplit[0], $arraysplit[1])
This replaces the array for every run.
Changing it to += gave another issue. It simply appended each individual element. I used this post's info to fix it, sort of forcing a 2d array: How to create array of arrays in powershell?.
$file = Get-Content .\allsystems.csv
$arraysplit = #()
$arrayfinal = #()
[int]$windows = 0
foreach ($thing in $file){
if ($thing.StartsWith("#")) {
continue
}
else {
$arraysplit = $thing.Split(":")
$arrayfinal += ,$arraysplit
}
}
foreach ($item in $arrayfinal){
if ($item[1] -contains 'NT'){
$windows++
}
else {
continue
}
}
$windows
1
I also changed the file around and added more instances of both NT and other random garbage. Seems it works fine.
I'd avoid making another ForEach loop for bumping count occurrences. Your $arrayfinal also rewrites everytime, so I used ArrayList.
$file = Get-Content "E:\Code\PS\myPS\2018\Jun\12\allSystems.csv"
$arrayFinal = New-Object System.Collections.ArrayList($null)
foreach ($thing in $file){
if ($thing.StartsWith("#")) {
continue
}
else {
$arraysplit = $thing -split ":"
if($arraysplit[1] -match "NT" -or $arraysplit[1] -match "Windows")
{
$arrayfinal.Add($arraysplit[1]) | Out-Null
}
}
}
Write-Host "Entries with 'NT' or 'Windows' $($arrayFinal.Count)"
I'm not sure if you want to keep 'Example', 'example2'... so I have skipped adding them to arrayfinal, assuming the goal is to count "NT" or "Windows" occurrances
The goal of this script is to count the total number of Windows servers.
I'd suggest the easy way: using cmdlets built for this.
$csv = Get-Content -Path .\file.csv |
Where-Object { -not $_.StartsWith('#') } |
ConvertFrom-Csv
#($csv.servertype).Where({ $_.Equals('NT') }).Count
# Compatibility mode:
# ($csv.servertype | Where-Object { $_.Equals('NT') }).Count
Replace servertype and 'NT' with whatever that header/value is called.

Set mutiple values in one parameter

I have written the following script to compare jobs that I have saved in a folder and compare them by name on the servers.
$submaporiginal = 'D:\Jobs_from_Server\TEST_Jobs_Aangepast'
$SqlServer1Name = "servername"
$SqlServer1 = New-Object Microsoft.SqlServer.Management.Smo.Server($SqlServer1Name)
$files = Get-ChildItem $submaporiginal *.sql
$SqlServer1JobListing = $SqlServer1.JobServer.Jobs |
Select-Object -ExpandProperty Name
foreach ($file in $files)
{
$stap1 = $file.Name
$locatiedtsx = $stap1.IndexOf("_goed")
$SqlServer2JobListing = $stap1.Substring(0,$locatiedtsx)
}
Compare-Object $SqlServer1JobListing $SqlServer2JobListing
{
if ($_.SideIndicator -eq "<=")
{Write-Host $_}
}
After running it shows a list with all the jobs that are currently on the server but are not saved in the folder: TEST_Jobs_Aangepast
My problem is that currently it only ignores the last job that have run trough the foreach ($file in $files)
For excample in the map are the files TestA.sql and TestB.SQL while the server has a job named testA, TestB and TestC.
The result shown should be:
TestC <=
but the actual result is shows:
TestA <=
TestC <=
My question is how can I set all the values that go trough foreach ($file in $files) in one parameter SqlServer2JobListing so that all the jobs that match between $SqlServer1JobListing $SqlServer2JobListing are not shown
Your problem is that you always reassign the $SqlServer2JobListing variable, instead you need to add a value to array or list. Like this:
$SqlServer1JobListing = New-Object System.Collections.ArrayList
foreach ($file in $files)
{
$stap1 = $file.Name
$locatiedtsx = $stap1.IndexOf("_goed")
$SqlServer1JobListing.Add($stap1.Substring(0,$locatiedtsx))
}
This way, after your foreach loop you will get a collection of all the items, rather than only the last one like you had before.

Powershell: Search data in *.txt files to export into *.csv

First of all, this is my first question here. I often come here to browse existing topics, but now I'm hung on my own problem. And I didn't found a helpful resource right now. My biggest concern would be, that it won't work in Powershell... At the moment I try to get a small Powershell tool to save me a lot of time. For those who don't know cw-sysinfo, it is a tool that collects information of any host system (e.g. Hardware-ID, Product Key and stuff like that) and generates *.txt files.
My point is, if you have 20, 30 or 80 server in a project, it is a huge amount of time to browse all files and just look for those lines you need and put them together in a *.csv file.
What I have working is more like the basic of the tool, it browses all *.txt in a specific path and checks for my keywords. And here is the problem that I just can use the words prior to those I really need, seen as follow:
Operating System: Windows XP
Product Type: Professional
Service Pack: Service Pack 3
...
I don't know how I can tell Powershell to search for "Product Type:"-line and pick the following "Professional" instead. Later on with keys or serial numbers it will be the same problem, that is why I just can't browse for "Standard" or "Professional".
I placed my keywords($controls) in an extra file that I can attach the project folders and don't need to edit in Powershell each time. Code looks like this:
Function getStringMatch
{
# Loop through the project directory
Foreach ($file In $files)
{
# Check all keywords
ForEach ($control In $controls)
{
$result = Get-Content $file.FullName | Select-String $control -quiet -casesensitive
If ($result -eq $True)
{
$match = $file.FullName
# Write the filename according to the entry
"Found : $control in: $match" | Out-File $output -Append
}
}
}
}
getStringMatch
I think this is the kind of thing you need, I've changed Select-String to not use the -quiet option, this will return a matches object, one of the properties of this is the line I then split the line on the ':' and trim any spaces. These results are then placed into a new PSObject which in turn is added to an array. The array is then put back on the pipeline at the end.
I also moved the call to get-content to avoid reading each file more than once.
# Create an array for results
$results = #()
# Loop through the project directory
Foreach ($file In $files)
{
# load the content once
$content = Get-Content $file.FullName
# Check all keywords
ForEach ($control In $controls)
{
# find the line containing the control string
$result = $content | Select-String $control -casesensitive
If ($result)
{
# tidy up the results and add to the array
$line = $result.Line -split ":"
$results += New-Object PSObject -Property #{
FileName = $file.FullName
Control = $line[0].Trim()
Value = $line[1].Trim()
}
}
}
}
# return the results
$results
Adding the results to a csv is just a case of piping the results to Export-Csv
$results | Export-Csv -Path "results.csv" -NoTypeInformation
If I understand your question correctly, you want some way to parse each line from your report files and extract values for some "keys". Here are a few lines to give you an idea of how you could proceede. The example is for one file, but can be generalized very easily.
$config = Get-Content ".\config.txt"
# The stuff you are searching for
$keys = #(
"Operating System",
"Product Type",
"Service Pack"
)
foreach ($line in $config)
{
$keys | %{
$regex = "\s*?$($_)\:\s*(?<value>.*?)\s*$"
if ($line -match $regex)
{
$value = $matches.value
Write-Host "Key: $_`t`tValue: $value"
}
}
}