I am trying to write a script that will get the names of all the folders in a specific directory and then return each as an entry in an array. From here I was going to use each array element to run a larger loop that uses each element as a parameter for a later function call. All of this is through powershell.
At the moment I have this code:
function Get-Directorys
{
$path = gci \\QNAP\wpbackup\
foreach ($item.name in $path)
{
$a = $item.name
}
}
The $path line is correct and gets me all of the directories, however the foreach loop is the problem where it actually stores the individual chars of the first directory instead of each directories full name to each element.
Here's another option using a pipeline:
$arr = Get-ChildItem \\QNAP\wpbackup |
Where-Object {$_.PSIsContainer} |
Foreach-Object {$_.Name}
$array = (dir *.txt).FullName
$array is now a list of paths for all text files in the directory.
For completeness, and readability:
This get all files in "somefolder" starting with 'F' to an array.
$FileNames = Get-ChildItem -Path '.\somefolder\' -Name 'F*' -File
This gets all directories of current directory:
$FileNames = Get-ChildItem -Path '.\' -Directory
# initialize the items variable with the
# contents of a directory
$items = Get-ChildItem -Path "c:\temp"
# enumerate the items array
foreach ($item in $items)
{
# if the item is a directory, then process it.
if ($item.Attributes -eq "Directory")
{
Write-Host $item.Name//displaying
$array=$item.Name//storing in array
}
}
I believe the problem is that your foreach loop variable is $item.name. What you want is a loop variable named $item, and you will access the name property on each one.
I.e.,
foreach ($item in $path)
{
$item.name
}
Also take note that I've left $item.name unassigned. In Powershell, if the result isn't stored in a variable, piped to another command, or otherwise captured, it is included in the function's return value.
Related
I have a script where I get all of the folders in d:\folder\*\*\ where the name is -like "*\Log". I then split the folder paths apart to run through wmi to get the corresponding services. After that I'm wanting to split apart the PathName property from $Services so I get everything before the \xxxxx.exe and add \log to the end of the result. Eventually I'll then use those paths to do some compression and archiving of files via a gci.
For whatever reason when I run the script below I the previous loops $LocalLogVar without "log" appended and the current loops LocalLogVar with log appended. I'm sure I'm doing something wrong that's blatantly obvious to somebody out there. If somebody could point me in the right direction on this it'd be much appreciated! I also apologize for the word vomit here, I've been looking at this script all day and my brain's pretty much used up.
A couple of notes:
The number of words in the paths vary which is why I can't manually do $LocalLogVar = "$Var1\$Var2\$Var3\Log"
If I don't have the [array] in front of $LogFolders the object type becomes a string and I get the previous loop's $LocalLogVar without "log" appended combined with the current loop's $LocalLogVar
I tried doing [collections.arraylist]$LogFolders=#() with no success
c:\folder is a shortcut to d:\folder, which is why there's c:\folder\xxx and d:\folder\xxx in the list below
SplitCount is -1 because I don't want the .exe from the path, I just want the folder structure
The naming convention for the string before .exe varies so I can't use an enumerated counter.
Example of first bullet:
word7-word8 #This is the previous loop's $LocalLogVar w/o "log" appended
C:\folder\word5\word6\word9-word8\log #This is the current loop's $LocalLogVar w/ "log" appended.
Example of the second bullet:
word7-word8C:\folder\word5\word6\word9-word8\log
What I should be getting:
D:\folder\word-anotherword\word7-word8\log
D:\folder\word-anotherword\word9-word8\log
C:\folder\word1\word7-word8\log
C:\folder\word1\word9-word8\log
C:\folder\word2\word7-word8\log
C:\folder\word2\word9-word8\log
D:\folder\word2\word10-word11\log
D:\folder\word2\word12-word8\log
C:\folder\word3\word7-word8\log
C:\folder\word3\word9-word8\log
D:\folder\word4\word7-word8\log
C:\folder\word4\word9-word8\log
C:\folder\word5\word6\word7-word8\log
C:\folder\word5\word6\word9-word8\log
C:\folder\word5\word6\word7-word8\log
C:\folder\word5\word6\word9-word8\log
$Folders = Get-ChildItem D:\folder\*\*\ -Directory -Recurse -Verbose `
| Where-Object { $_.fullname -like "*\Log" }
$2 = #()
$LogFolders = #()
foreach ($folder in $folders) {
$ServName = $folder.fullname.split('\')[2]
$ServType = $folder.fullname.split('\')[3]
$ServNameCheck = "*$($ServName.replace('-',' '))*"
$ServTypeCheck = "*$($ServType.replace('-',' '))*"
$PathName = Get-WmiObject -ClassName Win32_Service `
| Where-Object { $_.caption -like "$ServNameCheck" -and $_.caption -like "$ServTypeCheck" } `
| Select-Object Name, Caption, #{n = 'PathName'; e = { ($_.PathName).trim('"') } }
$2 += $PathName
}
$Services = $2 | Sort-Object pathname | Get-Unique -AsString
foreach ($ServPath in $services.pathname) {
$LocalLogVar = #()
if (Get-Variable `
| Where-Object { $_.name -match "^Split([0-9]|10)$" }) {
Get-Variable `
| Where-Object { $_.name -match "^Split([0-9]|10)$" } | Remove-Variable -Force
}
[int]$SplitCount = $ServPath.split('\').count
[int]$SplitCountCheck = $SplitCount - 1
$x = 0
do {
New-Variable -Name "Split$x" -Value "$($ServPath.split('\')[$x])"
$RegEx = "Split$x"
$LogFolderName = Get-Variable | Where-Object { $_.name -match $RegEx } | Select-Object value
[string]$LogFolders = $LogFolderName.value.ToString()
$LocalLogVar += $LogFolders + '\'
$x++
} until ($x -eq $SplitCountCheck)
$LocalLogVar = $LocalLogVar
$LocalLogVar = $LocalLogVar + "log"
[array]$LogFolders += $LocalLogVar
}
Wow, so that's a script. Kind of hard to follow, since some of it seems needlessly complex. I'm not sure if it will accomplish what you're looking for, but that's because you were super vague with your folder descriptions. Do the folders always start like this:
D:\folder<Service Short Name><Service Long Name>...\logs
If not you could be in trouble. The last four items on your example list of what you expect to see don't look like they are like that. I think the way your folders are laid out are like this:
D:\folder...<Service Short Name><Service Long Name>\logs
The difference being where the extra folders are located. If they're before the service like I think they are your script will miss things.
Now, on to getting your list that you want. What I see from looking at your script is that you get a folder list for all folders under D:\folder\*\*\ named 'log'. Then you split out the 3rd and 4th folders to get a service's short name, and long name respectively. Then one by one you pull a list of all services from WMI, and filter for just the service that matches the name and caption (short name, and long name) referred to by the folders. After that you make sure you only have one listing of any given service.
Regarding this first part of the script, you can make it faster by letting the file system provider filter things for you. Instead of pulling a folder list of everything and then filtering for paths that end in '\log', you should use the -filter parameter of the Get-ChildItem cmdlet like this:
$Folders = Get-ChildItem C:\temp\*\*\ -Directory -Recurse -Verbose -Filter 'log'
Then you should query WMI one time, save the result, then pick and choose from there based on your folders. Something like:
[array]$2 = foreach ($folder in $folders) {
$ServName,$ServType = $folder.fullname.split('\')[2,3] -replace '-',' '
$PathName = $AllServices |
Where-Object { $_.caption -like "*$ServName*" -and $_.caption -like "*$ServType*" } |
Select-Object Name, Caption, #{n = 'PathName'; e = { $_.PathName -replace '^(\w\S+) .*','$1' -replace '^([''"])([^\1]+)\1.*','$2' } }
}
$Services = $2 | Sort-Object pathname | Get-Unique -AsString
I did a little regex magic to clean up the pathname instead of just .trim('"') since this gets rid of parameters in the service execution, and cleans paths that are enclosed in single quotes not just double quotes. If what you have works for you feel free to keep it, but this is a little more capable. It may be worth noting that Get-Unique is case sensitive, so 'C:\folder\word3\word9-word8' and 'C:\folder\word3\word9-Word8' are different. You might want to do a .ToUpper() on your paths before you look for unique ones.
Once you have your array of services you loop through them, splitting the file path, reassembling it, and finally adding 'log' to the end of it. That was your way to remove the executable from the path. There's a cmdlet that was designed to do just that: split-path. Use that with Join-Path and that whole last loop gets much simpler:
[array]$LogFolders = foreach ($ServPath in $services.pathname) {
Join-Path (Split-Path $ServPath) 'log'
}
Lastly, try not to use +=, since PowerShell has to rebuild the whole array each time you do that. You'll notice I moved the $Variable = bit outside the loop in places that you do that.
I'm trying to populate an array of file paths where ever the script is located in. But I don't want
the array to include the path of the script only the other files in that folder. I have tried removing it after it is populated by using a list array instead but then I get an error that the array is a fixed size.
#To get path in which the script is located
$mypath = $MyInvocation.MyCommand.Path
$myStringPath=$mypath.ToString().Replace("TestingScriptPath.ps1", "")
#Populates files inside the folder
$array = #()
(Get-ChildItem -Path $myStringPath ).FullName |
foreach{
$array += $_
}
#display paths
for($i = 0; $i -lt $array.length; $i++)
{
$array[$i]
}
You're better off not putting it in the array in the first place.
When updating an array the whole array has to be rewritten, so the performance tends to be terrible.
Use a different datatype if you're going to be removing item-by-item.
#To get path in which the script is located
$mypath = $MyInvocation.MyCommand.Path
$myStringPath=$mypath.ToString().Replace("testingscriptpath.ps1", "")
#Populates files inside the folder
$array = Get-ChildItem -Path $myStringPath | Where-Object {$_.fullname -ne $mypath}
$array
if you definitely want to do it the way suggested in the question (slower)
$ArrayWithFile = Get-ChildItem -Path $myStringPath
$ArrayWithoutFile = $ArrayWithFile | Where-Object {$_.fullName -ne $mypath}
This question already has an answer here:
Powershell: Piping output of pracl command to array
(1 answer)
Closed 1 year ago.
Using Get-ChildItem I have pulled a list of files that meet a criteria, then split a part of the Basename and want to build an array with that part of the name. I can do that successfully, except the array returns on long string. I'd like each part of the array to return on a new line.
Script:
$files = GCI "\\Paths" -Recurse | Where-Object {$_.LastWriteTime -ge (Get-Date).Adddays(-22)}
$name = ""
foreach($file in $files){
$file = $file.basename.Split(".")[0]
$array += $file
}
I also tried the following with no luck:
$files = GCI "\\Paths" -Recurse | Where-Object {$_.LastWriteTime -ge (Get-Date).Adddays(-22)}
$name = ""
foreach($file in $files){
$file = $file.basename.Split(".")[0]
$array+= $file -split "`n"
}
Current outcome when calling $array:
file01file02file03file04
Desired outcome when calling $array:
file01
file02
file03
file04
The string is returned because $array is not an array. It is typed at assignment and its first assignment is a string. Therefore it keeps appending new values to that string.
You may do the following instead:
$array = foreach($file in $files){
$file.basename.Split(".")[0]
}
When iterated values are output within a foreach statement, that statement output can be captured into a variable. Each value will be an element of an array.
As an aside, the += syntax to add elements to an array is inefficient because a new array is created each time after retrieving all the contents of the current array.
You're already returning an array, so just narrow it down to what you're assigning to your variable.
$files = GCI "\\Paths" -Recurse |
Where-Object {$_.LastWriteTime -ge (Get-Date).Adddays(-22)} |
ForEach-Object -Process {
$_.basename.Split(".")[0]
}
Or, just assign a variable to your foreach loop removing the output to an array.:
$arr = foreach (...)
It seems Get-ChildItem returns a single object instead of an array of one object if it only finds one item. For example this returns 5:
$files = Get-ChildItem -Filter "e*.txt"
$files.length
But the following should return 1 but returns 61321:
$files = Get-ChildItem -Filter "exact.txt"
$files.length
The 61321 is the size in bytes of the file exact.txt.
How can we consistently check if there were files found?
It is a "feature" of Get-ChildItem that it won't return an array with one item but instead the single object. To force an array append # as in:
$files = #(Get-ChildItem -Filter "e*.txt")
Alternatively if you just want to check if there are NO files you can do:
$files = Get-ChildItem -Filter "exact.txt"
if (!$files) {"No Files"}
I am trying to do a mass import of tables into SQL. To do this I need to open a main directory and then cycle through those sub-directories to execute the actual files. I cannot seem to store these sub-directory names in a list.
Here is my code:
$path = "E:\Dictionary_SQL_Commands\";
$i = 0;
#I would like to dynamically allocate this space as I do not know the length
$global:contents = new-object object[] 10;
foreach ($item in $path){
$global:contents[$i] = $local:item.name;
$i++;
}
$contents gets created and I can view it using gci variable: but the output is {$null,$null,...}.
You don't need a "list". This is Powershell.
# Get all File items in the given path recursively #
Get-ChildItem -Path:$path -Recurse -File |
# Process each one as it's available #
Foreach-Object {
# Get the contents of each file #
$Contents = Get-Content $_;
# do other stuff with $Contents here #
}
This is the Powershell pipeline