if loop not seems to be working - powershell

I have a code as given below:
$datearray = #()
$temp = Get-Content "C:\temp.txt"
$temp1 = Get-Content "C:\temp1.txt"
foreach ($te in $temp) {
$t = $te -split '-'
$da = $t[1]
$mo = $t[2]
$yea = $t[3]
$fulldate = "$da-$mo-$yea"
if ($temp1 -match $fulldate) {
if ($fulldate -match $te) {
$datearray += $_
$fmt = 'dd-MM-yy-HH-mm'
$culture = [Globalization.CultureInfo]::InvariantCulture
*!* $datearray | sort { [DateTime]::ParseExact(($_ -split '-', 2)[1], $fmt, $culture) } | select -Last 1 | Add-Content "c:\temp4.txt"
} else {
#some operation
}
} else {
#some operation
}
}
For your understanding, I will show you how temp1.txt looks like:
17-07-15
18-07-15
19-07-15
20-07-15
21-07-15
22-07-15
23-07-15
temp.txt is:
testdatabase-17-07-15-22-00
testdatabase-17-07-15-23-00
testdatabase-21-07-15-10-00
testdatabase-21-07-15-23-00
What I am trying to do is that whenever it reaches the code marked with *!*, it goes back to foreach loop in the top every time. That marked code is not getting executed.
Can someone please tell me the solution?

Use the Group-Object cmdlet to group the databases by date, then select the most recent database name from each group:
$fmt = 'dd-MM-yy-HH-mm'
$culture = [Globalization.CultureInfo]::InvariantCulture
Get-Content 'C:\temp.txt' |
select #{n='Timestamp';e={[DateTime]::ParseExact(($_ -split '-', 2)[1], $fmt, $culture)}},
#{n='Database';e={$_}} |
group { $_.Timestamp.Date } |
% { $_.Group | sort Timestamp | select -Last 1 -Expand Database }
The code uses a select statement to transform the list of lines into a list of custom objects with a Timestamp and a Database property in order to simplify grouping and sorting the database names by date.
Inspecting the output after each step of the pipeline should help you understand the logic behind this. Get-Content produces a list of strings with the lines from the file:
PS C:\> Get-Content 'C:\temp.txt'
testdatabase-17-07-15-22-00
testdatabase-17-07-15-23-00
testdatabase-21-07-15-10-00
testdatabase-21-07-15-23-00
By using Select-Object with calculated properties the list of strings is transformed into a list of custom objects with 2 properties, the database name and the timestamp (as a DateTime object):
PS C:\> Get-Content 'C:\temp.txt' |
>> select #{n='Timestamp';e={[DateTime]::ParseExact(($_ -split '-', 2)[1], $fmt, $culture)}},
>> #{n='Database';e={$_}}
>>
Timestamp Database
--------- --------
17.07.2015 22:00:00 testdatabase-17-07-15-22-00
17.07.2015 23:00:00 testdatabase-17-07-15-23-00
21.07.2015 10:00:00 testdatabase-21-07-15-10-00
21.07.2015 23:00:00 testdatabase-21-07-15-23-00
Grouping these objects by the date portion of the timestamp gets you a list of GroupInfo objects whose Group property contains a list of the database names for a given date:
PS C:\> Get-Content 'C:\temp.txt' |
>> select #{n='Timestamp';e={[DateTime]::ParseExact(($_ -split '-', 2)[1], $fmt, $culture)}},
>> #{n='Database';e={$_}} |
>> group { $_.Timestamp.Date }
>>
Count Name Group
----- ---- -----
2 17.07.2015 00:00:00 {#{Timestamp=17.07.2015 22:00:00; Database=testdatabase-17-07-15-22-00}, #{Timestamp...
2 21.07.2015 00:00:00 {#{Timestamp=21.07.2015 10:00:00; Database=testdatabase-21-07-15-10-00}, #{Timestamp...
The ForEach-Object loop then sorts the elements of each group by timestamp and selects the last (most recent) database name from each group:
PS C:\> Get-Content 'C:\temp.txt' |
>> select #{n='Timestamp';e={[DateTime]::ParseExact(($_ -split '-', 2)[1], $fmt, $culture)}},
>> #{n='Database';e={$_}} |
>> group { $_.Timestamp.Date } |
>> % { $_.Group | sort Timestamp | select -Last 1 -Expand Database }
>>
testdatabase-17-07-15-23-00
testdatabase-21-07-15-23-00

Related

Datetime in PowerShell can't convert my Time

I'm writing a Script to compare two sets of timevalues and then calculate a exact time.
My problem is the calculation with timestamps. I import the times from a .csv-file. The times look like this:
08:37;
11:47;
12:11;
17:34;
etc.
I made a variable for the times so i always have the correct time from the correct line from the csv file.
My goal ist to calculate the time from one timestamp to another like this: 11:47 - 08:37 = 3:10
If i do this in my PowerShell Script an error occurs: The value "time=12:39" can not be converted to type "System.DateTime". Error: "The string was not recognized as a DateTime. An unknown word starts at index 1"
Is datetime wrong in this case? How can i make this work?
Thx for your help.
If this has to do with your previous question and the CSV actually looks like this:
name;prename;date;time
Gantz;Mario;09.02.;07:37
Gantz;Mario;09.02.;11:23
Gantz;Mario;09.02.;12:34
Gantz;Mario;09.02.;17:03
Then this should do it
# create two variables to hold the times parsed from the CSV, Initialize to $null
$current, $previous = $null
# load the csv and loop through the records
$result = Import-Csv -Path 'D:\Test\times.csv' -Delimiter ';' | ForEach-Object {
$current = [datetime]::ParseExact($_.time, 'HH:mm', $null)
if (!$previous) { $previous = $current }
# subtracting two DateTime objects results in a TimeStamp
$elapsed = $current - $previous
$previous = $current
# output the record with column 'elapsed' appended
$_ | Select-Object *, #{Name = 'elapsed'; Expression = {$elapsed.ToString()}}
}
# output on screen
$result | Format-Table -AutoSize
# output to new CSV file
$result | Export-Csv -Path 'D:\Test\times_and_intervals.csv' -Delimiter ';' -NoTypeInformation
Output on screen:
name prename date time elapsed
---- ------- ---- ---- -------
Gantz Mario 09.02. 07:37 00:00:00
Gantz Mario 09.02. 11:23 03:46:00
Gantz Mario 09.02. 12:34 01:11:00
Gantz Mario 09.02. 17:03 04:29:00
Now that I see you also have a 'date' column in there, you should include that in the conversion to [datetime] aswell:
# create two variables to hold the times parsed from the CSV, Initialize to $null
$current, $previous = $null
# load the csv and loop through the records
$result = Import-Csv -Path 'D:\Test\times.csv' -Delimiter ';' | ForEach-Object {
$completeDate = '{0}{1} {2}' -f $_.date, (Get-Date).Year, $_.time
$current = [datetime]::ParseExact($completeDate, 'dd.MM.yyyy HH:mm', $null)
if (!$previous) { $previous = $current }
# subtracting two DateTime objects results in a TimeStamp
$elapsed = $current - $previous
$previous = $current
# output the record with column 'elapsed' appended
$_ | Select-Object *, #{Name = 'elapsed'; Expression = {$elapsed.ToString()}}
}
# output on screen
$result | Format-Table -AutoSize
# output to new CSV file
$result | Export-Csv -Path 'D:\Test\times_and_intervals.csv' -Delimiter ';' -NoTypeInformation
You are getting the error because you are not specifying the values that you are importing as [datetime]
I have replicated the error where I just specified 2 time values and subtracted them:
$st = "08:37" $et = "11:47" $di = $st - $et Cannot convert value
"08:37" to type "System.Int32". Error: "Input string was not in a
correct format."
Solution:
Specify the values of each entry like so:
[datetime]$starttime = "08:37"
[datetime]$endtime = "11:47"
$diff = $endtime - $starttime
If you just want the time in minutes etc. you can enter $diff.Minutes respectively
Hope this works for you.

How to export two variables into same CSV as joined via PowerShell?

I have a PowerShell script employing poshwsus module like below:
$FileOutput = "C:\WSUSReport\WSUSReport.csv"
$ProcessLog = "C:\WSUSReport\QueryLog2.txt"
$WSUSServers = "C:\WSUSReport\Computers.txt"
$WSUSPort = "8530"
import-module poshwsus
ForEach ($Server in Get-Content $WSUSServers)
{
& connect-poshwsusserver $Server -port $WSUSPort | out-file $ProcessLog -append
$r1 = & Get-PoshWSUSClient | select #{name="Computer";expression={$_.FullDomainName}},#{name="LastUpdated";expression={if ([datetime]$_.LastReportedStatusTime -gt [datetime]"1/1/0001 12:00:00 AM") {$_.LastReportedStatusTime} else {$_.LastSyncTime}}}
$r2 = & Get-PoshWSUSUpdateSummaryPerClient -UpdateScope (new-poshwsusupdatescope) -ComputerScope (new-poshwsuscomputerscope) | Select Computer,NeededCount,DownloadedCount,NotApplicableCount,NotInstalledCount,InstalledCount,FailedCount
}
What I need to do is to export CSV outpout including the results with the columns (like "inner join"):
Computer, NeededCount, DownloadedCount, NotApplicableCount, NotINstalledCount, InstalledCount, FailedCount, LastUpdated
I have tried to use the line below in foreach, but it didn't work as I expected.
$r1 + $r2 | export-csv -NoTypeInformation -append $FileOutput
I appreciate if you may help or advise.
EDIT --> The output I've got:
ComputerName LastUpdate
X A
Y B
X
Y
So no error, first two rows from $r2, last two rows from $r1, it is not joining the tables as I expected.
Thanks!
I've found my guidance in this post: Inner Join in PowerShell (without SQL)
Modified my query accordingly like below, works like a charm.
$FileOutput = "C:\WSUSReport\WSUSReport.csv"
$ProcessLog = "C:\WSUSReport\QueryLog.txt"
$WSUSServers = "C:\WSUSReport\Computers.txt"
$WSUSPort = "8530"
import-module poshwsus
function Join-Records($tab1, $tab2){
$prop1 = $tab1 | select -First 1 | % {$_.PSObject.Properties.Name} #properties from t1
$prop2 = $tab2 | select -First 1 | % {$_.PSObject.Properties.Name} #properties from t2
$join = $prop1 | ? {$prop2 -Contains $_}
$unique1 = $prop1 | ?{ $join -notcontains $_}
$unique2 = $prop2 | ?{ $join -notcontains $_}
if ($join) {
$tab1 | % {
$t1 = $_
$tab2 | % {
$t2 = $_
foreach ($prop in $join) {
if (!$t1.$prop.Equals($t2.$prop)) { return; }
}
$result = #{}
$join | % { $result.Add($_,$t1.$_) }
$unique1 | % { $result.Add($_,$t1.$_) }
$unique2 | % { $result.Add($_,$t2.$_) }
[PSCustomObject]$result
}
}
}
}
ForEach ($Server in Get-Content $WSUSServers)
{
& connect-poshwsusserver $Server -port $WSUSPort | out-file $ProcessLog -append
$r1 = & Get-PoshWSUSClient | select #{name="Computer";expression={$_.FullDomainName}},#{name="LastUpdated";expression={if ([datetime]$_.LastReportedStatusTime -gt [datetime]"1/1/0001 12:00:00 AM") {$_.LastReportedStatusTime} else {$_.LastSyncTime}}}
$r2 = & Get-PoshWSUSUpdateSummaryPerClient -UpdateScope (new-poshwsusupdatescope) -ComputerScope (new-poshwsuscomputerscope) | Select Computer,NeededCount,DownloadedCount,NotApplicableCount,NotInstalledCount,InstalledCount,FailedCount
Join-Records $r1 $r2 | Select Computer,NeededCount,DownloadedCount,NotApplicableCount,NotInstalledCount,InstalledCount,FailedCount, LastUpdated | export-csv -NoTypeInformation -append $FileOutput
}
I think this could be made simpler. Since Select-Object's -Property parameter accepts an array of values, you can create an array of the properties you want to display. The array can be constructed by comparing your two objects' properties and outputting a unique list of those properties.
$selectProperties = $r1.psobject.properties.name | Compare-Object $r2.psobject.properties.name -IncludeEqual -PassThru
$r1,$r2 | Select-Object -Property $selectProperties
Compare-Object by default will output only differences between a reference object and a difference object. Adding the -IncludeEqual switch displays different and equal comparisons. Adding the -PassThru parameter outputs the actual objects that are compared rather than the default PSCustomObject output.

Group by and add or subtract depending on the indicator

I have a file which has transaction_date, transaction_amount and debit_credit_indicator. I want to write a program which shows for each date total count and total amount.
Total amount is calculated as follows -
if debit_credit_indicator is 'C' add else if 'D' subtract.
I got till grouping by indicators but don't know how to proceed after wards.
My ouput looks like this
TRANSACTION_DATE DEBIT_CREDIT_INDICA TotalAmount Count
TOR
---------------- ------------------- ----------- -----
2019-02-26 C 1478
2019-02-25 D 100
2019-02-26 D 200
param([string]$inputFileName=30)
(Get-Content $inputFileName) -replace '\|', ',' | Set-Content c:\learnpowershell\test.csv
$transactionData = Import-csv c:\learnpowershell\test.csv | Group-Object -Property TRANSACTION_DATE, DEBIT_CREDIT_INDICATOR
[Array] $newsbData += foreach($gitem in $transactionData)
{
$gitem.group | Select -Unique TRANSACTION_DATE, DEBIT_CREDIT_INDICATOR, `
#{Name = ‘TotalAmount’;Expression = {(($gitem.group) | measure -Property TRANSACTION_AMOUNT -sum).sum}},
#{Name = ‘Count’;Expression = {(($gitem.group) | Measure-Object -count).count}}
};
write-output $newsbData
I suppose you want replace '|' by ',' because you dont know -delimiter option otherwise keep you code for replace. now i propose my code for your problem:
#import en group by date
import-csv "c:\learnpowershell\test.csv" -Delimiter '|' | group TRANSACTION_DATE | %{
$TotalCredit=0
$TotalDebit=0
$CountRowCredit=0
$CountRowDebit=0
$HasProblem=$false
#calculation by date for every group
$_.Group | %{
if ($_.DEBIT_CREDIT_INDICATOR -EQ 'C')
{
$TotalCredit+=$_.transaction_amount
$CountRowCredit++
}
elseif ($_.DEBIT_CREDIT_INDICATOR -EQ 'D')
{
$TotalDebit+=$_.transaction_amount
$CountRowDebit++
}
else
{
$HasProblem=$true
}
}
#output result
[pscustomobject]#{
TRANSACTION_DATE=$_.Name
CountRow=$_.Count
Credit_Total=$TotalCredit
Credit_CountRow=$CountRowCredit
Debit_Total=-$TotalDebit
Debit_CountRow=$CountRowDebit
Total_DebitCredit=$TotalCredit - $TotalDebit
HasProblem=$HasProblem
}
}
You can add ' | Format-Table ' if you want print result formated in table

Format-Table for array of hashtables

I am trying to use the Format-Table command to output an array of hash tables of all files checked out from our TFS repo.
My code thus far:
$arr = #();
#Take the string from the tf command, parse it and build an array of hash tables
(tf stat /recursive /user:* /format:detailed | Select-String -Pattern '^\$' -NotMatch | Select -SkipLast 3 | Out-String) -split '(\r\n){2}' | ForEach-Object {
$ht = #{};
if ($_ -ne '') {
$str = $_ | Out-String;
$str -split '\r?\n'| ForEach-Object {
$key, $value = $_ -split '\s*:\s*';
#Write-Host $key, $Value;
try {
$ht.Add($key, $value);
} catch [ArgumentException] {
Write-Host "Caught exception";
}
}
$arr += ($ht);
}
}
Edit
Looks like I'm erroring out here.
$arr.ForEach({[PSCustomObject]$_}) | Format-Table -AutoSize
Full Error:
Cannot convert value "System.Collections.Hashtable" to type
"System.Management.Automation.LanguagePrimitives+InternalPSCustomObject". Error: "Cannot process argument because the
value of argument "name" is not valid. Change the value of the "name" argument and run the operation again."
At C:\Dev\Tools\powershell\Convert-TfsOutput.ps1:21 char:15
+ $arr.ForEach({[PSCustomObject]$_}) | Format-Table -AutoSize
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvalidCastConstructorException
Edit2
Here is sample output when i replace the above line with:
$arr.ForEach({ $_ | Out-String })
Name Value
---- -----
Workspace work1
Date {Wednesday, September 5, 2018 1, 38, 48 PM}
Local item file1
File type Windows-1252
User user1
Lock none
Change edit
Name Value
---- -----
Workspace work2
Date {Monday, September 10, 2018 12, 14, 56 PM}
Local item file2
User user2
Lock none
Change edit
Edit 3
Output of the below command
Write-Host $str;
User : User1
Date : Wednesday, September 5, 2018 1:38:48 PM
Lock : none
Change : edit
Workspace : Work1
Local item : File1
File type : Windows-1252
User : User2
Date : Monday, September 10, 2018 12:14:56 PM
Lock : none
Change : edit
Workspace : Work2
Local item : File2
Would like the output in a tabular format with rows below the column names:
Workspace | Date | Local item | File type | User | Lock | Change
Tried to use the code in another answer but it does not output correctly.
Format-Table on Array of Hash Tables
Convert your hashtables to custom objects before passing them to Format-Table.
... | Where-Object { $_ } | ForEach-Object {
$ht = #{};
($_ | Out-String) -split '\r?\n'| ForEach-Object {
...
}
New-Object -Type PSObject -Property $ht
} | Format-Table
Edit: Looks like your input data has blank lines which lead to keys with empty strings in your hashtables, which then cause the error you observed, because objects can't have a property with an empty string for a name.
Change your hashtable/object creation to something like this:
... | Where-Object { $_ } | ForEach-Object {
$ht = ($_ | Out-String).Trim() -replace '\s+:\s+', '=' |
ConvertFrom-StringData
New-Object -Type PSObject -Property $ht
} | Format-Table

Parse info from a text file

I am running this command to pull the last line of a log file:
Get-Content c:\temp\MigrationJobStatus-20171020-123839-515.log |
Select-Object -Last 1
The results do give me the last line, but now I need to filter the results:
10/20/2017 12:38:56 PM Information [Event]: [JobEnd], [JobId]: [70b82296-b6e2-4539-897d-c46384619059], [Time]: [10/20/2017 12:38:49.074], [FilesCreated]: [0], [BytesProcessed]: [0], [ObjectsProcessed]: [34], [TotalExpectedSPObjects]: [34], [TotalErrors]: [19], [TotalWarnings]: [3], [TotalRetryCount]: [0], [MigrationType]: [None], [MigrationDirection]: [Import], [CreatedOrUpdatedFileStatsBySize]: [{}], [ObjectsStatsByType]: [{"SPUser":{"Count":1,"TotalTime":0,"AccumulatedVersions":0,"ObjectsWithVersions":0},"SPFolder": "Count":4,"TotalTime":629,"AccumulatedVersions":0,"ObjectsWithVersions":0},"SPDocumentLibrary":"Count":1,"TotalTime":68,"AccumulatedVersions":0,"ObjectsWithVersions":0},"SPFile":{"Count":13,"TotalTime":0,"AccumulatedVersions":0,"ObjectsWithVersions":0},"SPListItem":{"Count":16,"TotalTime":2240,"AccumulatedVersions":0,"ObjectsWithVersions":0}}], [CorrelationId]: [7bbf249e-701a-4000-8eee-c4a7ef172063]
I need to be able to pull the following and export to CSV:
[JobId]: [70b82296-b6e2-4539-897d-c46384619059]
[FilesCreated]: [0]
[BytesProcessed]: [0]
[ObjectsProcessed]: [34]
[TotalExpectedSPObjects]: [34]
[TotalErrors]: [19]
[TotalWarnings]: [3]
Can someone give me some ideas on how to accomplish this?
I am doing a OneDrive 4 Business migration and need to pull the results of the Get-SPOMigrationJobProgress log for a few thousand users.
Need to add other fields there and then save results using Out-File
$results = ""
$fields = #("[JobId]", "[FilesCreated]")
$items = get-content c:\temp\MigrationJobStatus-20171020-123839-515.log | select-object -last 1 | %{ $_.Split(",")}
foreach($item in $items)
{
$field = ($item.Split(":")[0]).Trim()
if($fields.Contains($field)) { $results+= "$item`r`n" }
}
Write-Host $results
You can use split and grab the fields you need.
$text = get-content c:\temp\MigrationJobStatus-20171020-123839-515.log | select-object -last 1
$text = ($text -split ",").Trim(" ")
$csvtext = #"
$($text[3])
$($text[4])
$($text[5])
$($text[6])
$($text[7])
$($text[8])
"#
$csvtext | Out-File ".\logfile.csv"
You can get to the fields that you want by using regular expression and then create a psobject from each match:
$regexPattern = '\[([^]]+)\]: \[([^]]+)\]'
$result = Get-Content c:\temp\MigrationJobStatus-20171020-123839-515.log |
Select-Object -Last 1 |
Select-String -Pattern $regexPattern -AllMatches |
ForEach-Object { $_.Matches.Value } |
ForEach-Object { $_ -match $regexPattern |
Select-Object #{n='Name';e={$Matches[1]}},#{n='Value';e={$Matches[2]}} }
You can filter down the resulting object collection with Where-Object and use Export-Csv to get your result into a csv file.