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 (...)
Related
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}
I have a powershell script and a txt database with different number of elements per line.
My txt file is list.txt:
"10345","doomsday","life","hope","run","stone"
"10346","ride","latest","metal"
My powershell script search.ps1:
#Get file path
$path = Split-Path $script:MyInvocation.MyCommand.Path
$search = #()
Get-Content -LiteralPath "$path\list.txt" | ForEach-Object {
$search += $_
}
So, how to convert each line as a element of array? As this:
$search = #(("10345","doomsday","life","hope","run","stone"),("10346","ride","latest","metal"))
To operate as:
echo $search[0][0]
Here's a concise PSv4+ solution:
$search = (Get-Content -LiteralPath $path\list.txt).ForEach({ , ($_ -split ',') })
The .ForEach() method operates on each line read from the input file by Get-Content.
$_ -split ',' splits each line into an array of strings by separator ,
, (...) wraps this array in an aux. single-item array to ensure that the array is effectively output as a whole, resulting in an array of arrays as the overall output.
Note: Strictly speaking, the .ForEach() method outputs a [System.Collections.ObjectModel.Collection[psobject]] collection rather than a regular PowerShell array ([object[]]), but for all practical purposes the two types act the same.
Note: The .ForEach() method was chosen as a faster alternative to a pipeline with the ForEach-Object (%) cmdlet.
Note that the .ForEach() method requires storing the input collection in memory as a whole first.
A faster and more memory-efficient, though perhaps slightly obscure alternative is to use a switch statement with the -file option:
$search = switch -file $path\list.txt { default { , ($_ -split ',') } }
switch -file processes each line of the specified file.
Since each line should be processed, only a default branch is used, in which the desired splitting is performed.
Use -split. A code snippet you can debug in ISE or VSCode below.
$x1 = #'
"10345","doomsday","life","hope","run","stone"
"10346","ride","latest","metal"
'#
$data = $x1 -split "`r`n"
$data.Count
$data[0] -split ","
$arr = #()
foreach ($row in $data)
{
$arr += ,($row -split ",")
}
"arr"
$arr
"0,3"
$arr[0][3]
"1,3"
$arr[1][3]
So you can split each line in your file returned from Get-Content and add it to your new array which lets you reference how you wanted...
There are other ways you can use your data depending on your needs.
Assuming you do not want each item quoted, you might consider to not using the -Split operator but just evaluating each line with the Invoke-Expression cmdlet or using a more secure [ScriptBlock] for this:
$Search = Get-Content ".\list.txt" | ForEach-Object {,#(&([ScriptBlock]::Create($_)))}
I'm relatively new to powershell and coding and am having issues accessing the values in an array. I'm trying to loop thru a set of files using foreach and count the number of messages in each file. And then have the count for each file put in to an array so I can assign it to a variable. When I do write-host $data[0] it returns all the values. If I do write-host $data1 it returns nothing. It seems like these values are all being stored as one instead of as individual numbers. How do I get each value and then assign it to a variable. Any help would be appreciated.
$FilePath = 'some file path here'
$TodaysDate = (Get-Date -format "MM-dd-yyyy")
ForEach($file in Get-ChildItem $FilePath -exclude *.ps1,*.xml,*.xls | Where-Object {$_.LastWriteTime -ge $TodaysDate})
{
$data = ,#(Get-Content $file | Where-Object {$_.Contains("MSH|")}).Count
write-host $data[0]
}
exit
powershell result
In this line:
$data = ,#(Get-Content $file | Where-Object {$_.Contains("MSH|")}).Count
you are creating an array of a single element (the count). What you want to do is add to $data each time:
$data += ,#(Get-Content $file | Where-Object {$_.Contains("MSH|")}).Count
But given your description, I think you may want a hashtable, using the filename as a key:
$data = #{} #init hashtable
ForEach($file in Get-ChildItem $FilePath -exclude *.ps1,*.xml,*.xls | Where-Object {$_.LastWriteTime -ge $TodaysDate})
{
$data[$file] = #(Get-Content $file | Where-Object {$_.Contains("MSH|")}).Count
}
write-output $data
I am trying to parse robocopy log files to get file size, path, and date modified. I am getting the information via regex with no issues. However, for some reason, I am getting an array with a single element, and that element contains 3 hashes. My terminology might be off; I am still learning about hashes. What I want is a regular array with multple elements.
Output that I am getting:
FileSize FilePath DateTime
-------- -------- --------
{23040, 36864, 27136, 24064...} {\\server1\folder\Test File R... {2006/03/15 21:08:01, 2010/12...
As you can see, there is only one row, but that row contains multiple items. I want multiple rows.
Here is my code:
[regex]$Match_Regex = "^.{13}\s\d{4}/\d{2}/\d{2}\s\d{2}:\d{2}:\d{2}\s.*$"
[regex]$Replace_Regex = "^\s*([\d\.]*\s{0,1}\w{0,1})\s(\d{4}\/\d{2}\/\d{2}\s\d{2}:\d{2}:\d{2})\s(.*)$"
$MainContent = New-Object System.Collections.Generic.List[PSCustomObject]
Get-Content $Path\$InFile -ReadCount $Batch | ForEach-Object {
$FileSize = $_ -match $Match_Regex -replace $Replace_Regex,('$1').Trim()
$DateTime = $_ -match $Match_Regex -replace $Replace_Regex,('$2').Trim()
$FilePath = $_ -match $Match_Regex -replace $Replace_Regex,('$3').Trim()
$Props = #{
FileSize = $FileSize;
DateTime = $DateTime;
FilePath = $FilePath
}
$Obj = [PSCustomObject]$Props
$MainContent.Add($Obj)
}
$MainContent | % {
$_
}
What am I doing wrong? I am just not getting it. Thanks.
Note: This needs to be as fast as possible because I have to process millions of lines, which is why I am trying System.Collections.Generic.List.
I think the problem is that for what you're doing you actually need two foreach-object loops. Using Get-Content with -Readcount is going to give you an array of arrays. Use the -Match in the first Foreach-Object to filter out the records that match in each array. That's going to give you an array of the matched records. Then you need to foreach through that array to create one object for each record:
[regex]$Match_Regex = "^.{13}\s\d{4}/\d{2}/\d{2}\s\d{2}:\d{2}:\d{2}\s.*$"
[regex]$Replace_Regex = "^\s*([\d\.]*\s{0,1}\w{0,1})\s(\d{4}\/\d{2}\/\d{2}\s\d{2}:\d{2}:\d{2})\s(.*)$"
$MainContent =
Get-Content $Path\$InFile -ReadCount $Batch |
ForEach-Object {
$_ -match $Match_Regex |
ForEach-Object {
$FileSize = $_ -replace $Replace_Regex,('$1').Trim()
$DateTime = $_ -replace $Replace_Regex,('$2').Trim()
$FilePath = $_ -replace $Replace_Regex,('$3').Trim()
[PSCustomObject]#{
FileSize = $FileSize
DateTime = $DateTime
FilePath = $FilePath
}
}
}
You don't really need to use the collection as an accumulator, just output PSCustomObjects, and let them accumulate in the result variable.
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.