formatting the output of a .txt in powershell - powershell

After a week of attempts I have to ask you this.
File input:
DD/MM
27,28
14,21
1
15
7
12
2,15
25
Each line of this file represents a month, so even if it´s empty, it should still count so the formatting can happen. Then based on file input, the desired output is:
Desired Output:
DD/MM
27/02
28/02
14/04
21/04
01/05
15/06
07/09
12/10
02/11
15/11
25/12
What I got so far and stuck here:
#getting the content into an array and formatting the .DAT file
$lines = Get-Content $outfileBR
If ($lines[0] -eq "DD/MM") {
$HEADER = $lines[0] + $linebreak
}
If ($lines[1] -eq '') {
continue
} Else {
$BRFILE = $lines[1].SUBSTRING(0,2) + "/01" + $linebreak
$BRFILE += $lines[1].SUBSTRING(3,2) + "/01" + $linebreak
}
If ($lines[2] -eq '') {
continue
} Else {
$BRFILE2 = $lines[2].SUBSTRING(0,2) + "/02" + $linebreak
$BRFILE2 += $lines[2].SUBSTRING(3,2) + "/02" + $linebreak
}
If ($lines[3] -eq '') {
continue
} Else {
$BRFILE3 = $lines[3].SUBSTRING(0,2) + "/03" + $linebreak
$BRFILE3 += $lines[3].SUBSTRING(3,2) + "/03" + $linebreak
}
Set-Content $BRdatFile ($HEADER + $BRFILE + $BRFILE2 + $BRFILE3)
Result:
DD/MM
/01
/01
27/02
28/02
/03
/03
Like I said, each line refers to a month, but if the line is empty (like shown in input file) I´ll not show it in the output. But on my result it´s appearing as /01 for January, /03 for march and so on.
What am I doing wrong, please?

Very similar to the answer given by Ronald Rink 'd-fens', I would do a loop within a loop, but since we know how many lines there should be in the file, I would do this:
#Get content of input file
$FileIn = Get-Content C:\Path\To\File.txt
#Start array for output with header record
[array]$FileOut += 'DD/MM'
#Loop 12 times, once for each month
ForEach($Month in (1..12)){
#Split the relevant line, and for each entry add a line to the output file
If([string]::IsNullOrEmpty($FileIn[$Month])){Continue}
$FileIn[$Month].Split(',') | ForEach{
$FileOut += '{0}/{1}' -f $_, $Month
}
}
#Output the new file
$FileOut | Set-Content C:\Path\To\NewFile.txt
Edit: I fixed 2 issues. I had [1..2] which should have been (1..12), and used the $_ reference instead of $Month (which should work anyway, but it's bad form imho).

Achieving this in PowerShell is as easy as this:
"DD/MM";
$lines = Get-Content $ENV:TEMP\input.txt;
for($c = 1; $c -lt $lines.Count; $c++)
{
$line = $lines[$c];
if(!$line) { continue; }
$line.Split(',') | % { '{0:00}/{1:00}' -f [int] $_, $c }
}
27/02
28/02
14/04
21/04
01/05
15/06
07/08
12/09
02/10
15/10
25/11
Edit: fixed $day/ $_

Related

Want to combine data from multiple text files with some logic per line, then output to a csv file

Similar to what I would do with XLS VLOOKUP's, I want to use data from a first imported (text or csv) file, lookup additional data into another imported object, and append data from 2nd object to the available fields in 1st imported file.
Then write it all to a .CSV file.
I'm new to powershell, so any help is welcome.
I started already with this code
cls
#Date
$date = Get-Date -Format yyyyMMddHHmm #ss
#files
$ProductionORderFile = "C:\Mydir\prod-orders.txt"
$UOMFile = "C:\Mydir\uom.txt"
#file_Import
$ProductionOrders = get-content -Path $ProductionOrderFile
$UOMs = import-csv -Path $UOMFile -Delimiter '|' -header UOM_SKU, UOM_UOM, UOM_UOMT, UOM_Qty, UOM_StdUOM, UOM_StdUOMT, UOM_EAN, UOM_kg, UOM_kgT
#main_loop
foreach ($line in $ProductionOrders)
{
$PO_ProdOrder = $line.SubString(0,10)
$PO_ProdOrderSKU = $line.SubString(11,10)
$PO_ProdOrderCust = $line.SubString(21,8)
$UOM_Found = "false"
$DLUOMQTY = 0
foreach ($UOM in $UOMs)
{
If ($PO_ProdOrderSKU = $UOM.UOM_SKU -And $UOM_UOM = "KAR")
{
$UOM_Found = "found"
break
}
if ($UOM_FOUND <> "found")
If ($PO_ProdOrderSKU = $UOM.UOM_SKU -And $UOM_UOM = "LTP")
{
$UOM_Found = "found"
break
}
if ($UOM_FOUND <> "found")
If ($PO_ProdOrderSKU = $UOM.UOM_SKU -And $UOM_UOM = "DIS")
{
$UOM_Found = "found"
break
}
}
if ($UOM_FOUND = "found)
{
$DLUOMQTY = $UOM.UOM_qty
}
$SavePath = "C:\Other\Temp\2207\rapid-sap-uom2" + "_" + $date + ".csv"
So, after this step, I want to save my line with all data from 1st file (prod-orders), preferably by column, and then add some columns from 2nd file uom as per above logic .
How to proceed ?
many thanks in advance for guiding a new user.

Powershell - F5 iRules -- Extracting iRules

I received a config file of a F5 loadbalancer and was asked to parse it with PowerShell so that it creates a .txt file for every iRule it finds. I'm very new to parsing and I can't seem to wrap my head around it.
I managed to extract the name of every rule and create a separate .txt file, but I am unable to wring the content of the rule to it. Since not all rules are identical, I can't seem to use Regex.
Extract from config file:
ltm rule /Common/irule_name1 {
SOME CONTENT
}
ltm rule /Common/irule_name2 {
SOME OTHER CONTENT
}
What I have for now
$infile = "F5\config_F5"
$ruleslist = Get-Content $infile
foreach($cursor in $ruleslist)
{
if($cursor -like "*ltm rule /*") #new object started
{
#reset all variables to be sure
$content=""
#get rulenames
$rulenameString = $cursor.SubString(17)
$rulename = $rulenameString.Substring(0, $rulenameString.Length -2)
$outfile = $rulename + ".irule"
Write-Host $outfile
Write-Host "END Rule"
#$content | Out-File -FilePath "F5/irules/" + $outfile
}
}
How can I make my powershell script read out what's between the brackets of each rule? (In this case "SOME CONTENT" & "SOME OTHER CONTENT")
Generally parsing involves converting a specific input ("string") into an "object" which PowerShell can understand (such as HTML, JSON, XML, etc.) and traverse by "dotting" through each object.
If you are unable to convert it into any known formats (I am unfamiliar with F5 config files...), and need to only find out the content between braces, you can use the below code.
Please note, this code should only be used if you are unable to find any other alternative, because this should only work when the source file used is code-correct which might not give you the expected output otherwise.
# You can Get-Content FileName as well.
$string = #'
ltm rule /Common/irule_name1 {
SOME CONTENT
}
ltm rule /Common/irule_name2 {
SOME OTHER CONTENT
}
'#
function fcn-get-content {
Param (
[ Parameter( Mandatory = $true ) ]
$START,
[ Parameter( Mandatory = $true ) ]
$END,
[ Parameter( Mandatory = $true ) ]
$STRING
)
$found_content = $string[ ( $START + 1 ) .. ( $END - 1 ) ]
$complete_content = $found_content -join ""
return $complete_content
}
for( $i = 0; $i -lt $string.Length; $i++ ) {
# Find opening brace
if( $string[ $i ] -eq '{' ) {
$start = $i
}
# Find ending brace
elseif( $string[ $i ] -eq '}' ) {
$end = $i
fcn-get-content -START $start -END $end -STRING $string
}
}
For getting everything encompassed within braces (even nested braces):
$string | Select-String '[^{\}]+(?=})' -AllMatches | % { $_.Matches } | % { $_.Value }
To parse data with flexible structure, one can use a state machine. That is, read data line by line and save the state in which you are. Is it a start of a rule? Actual rule? End of rule? By knowing the current state, one can perform actions to the data. Like so,
# Sample data
$data = #()
$data += "ltm rule /Common/irule_name1 {"
$data += "SOME CONTENT"
$data += "}"
$data += "ltm rule /Common/irule_withLongName2 {"
$data += "SOME OTHER CONTENT"
$data += "SOME OTHER CONTENT2"
$data += "}"
$data += ""
$data += "ltm rule /Common/irule_name3 {"
$data += "SOME DIFFERENT CONTENT"
$data += "{"
$data += "WELL,"
$data += "THIS ESCALATED QUICKLY"
$data += "}"
$data += "}"
# enum is used for state tracking
enum rulestate {
start
stop
content
}
# hashtable for results
$ht = #{}
# counter for nested rules
$nestedItems = 0
# Loop through data
foreach($l in $data){
# skip empty lines
if([string]::isNullOrEmpty($l)){ continue }
# Pick the right state and keep count of nested constructs
if($l -match "^ltm rule (/.+)\{") {
# Start new rule
$state = [rulestate]::start
} else {
# Process rule contents
if($l -match "^\s*\{") {
# nested construct found
$state = [rulestate]::content
++$nestedItems
} elseif ($l -match "^\s*\}") {
# closing bracket. Is it
# a) closing nested
if($nestedItems -gt 0) {
$state = [rulestate]::content
--$nestedItems
} else {
# b) closing rule
$state = [rulestate]::stop
}
} else {
# ordinary rule data
$state = [rulestate]::content
}
}
# Handle rule contents based on state
switch($state){
start {
$currentRule = $matches[1].trim()
$ruledata = #()
break
}
content {
$ruledata += $l
break
}
stop {
$ht.add($currentRule, $ruledata)
break
}
default { write-host "oops! $state" }
}
write-host "$state => $l"
}
$ht
Output rules
SOME CONTENT
SOME OTHER CONTENT
SOME OTHER CONTENT2
SOME DIFFERENT CONTENT
{
WELL,
THIS ESCALATED QUICKLY
}

PowerShell Nested Loop inner Loop doesnt work?

Hi first Question on PowerShell,
I need to write a script that takes a log file and do some calculation in it.
I am reading the log file correctly and then I am creating two arrays that only contain the lines that I need from the file.
$inVar = Select-String -path C:\Dev\Script\Ldorado.log -pattern "IN:"
$outVar = Select-String -path C:\Dev\Script\Ldorado.log -pattern "OUT:"
I get back every line where there is an IN in 1 array and OUT in the other.
I managed to split the lines to only get the dates in each line - by using .Lines.Split("(")
If I have the two date values from the array I can calc the timespan with New-TimeSpan and some converting.
Now I need to somehow do this for every entry there is inside the file that matches the pattern, so I think I need a Loop or even a nested Loop. I tried every Loop (for / foreach / foreachObject / while...) but with no luck.
For example if you look at this code:
foreach ($out in $outVar) {
$testVar = $out.Line.Split("(")[0]
foreach ($in in $inVar) {
$testIn = $in.Line.Split("(")[0]
}
Write-Host $testVar + $testIn
}
my Output looks like this:
12:55:49 + 13:20:31
12:55:49 + 13:20:31
12:55:49 + 13:20:31
13:54:35 + 13:20:31
13:54:35 + 13:20:31
13:54:35 + 13:20:31
16:37:44 + 13:20:31
16:37:44 + 13:20:31
16:37:44 + 13:20:31
17:15:23 + 13:20:31
17:15:23 + 13:20:31
17:15:23 + 13:20:31
9:06:13 + 13:20:31
9:06:13 + 13:20:31
...
Having every value 3 times is correct because there are always IN's and 3OUT's for every log.
The outer loop seems to work correctly but the nested Loop just takes the last value there is and prints it - instead I want to have the same behavior as the outer loop where it actually loops.
Anyone knows how I can solve this?
Use a for loop so you can grab the values at the same index from each array (this assumes $outVar and $inVar are aligned and of the same length):
for($i = 0; $i -lt $outVar.Length; $i++) {
$testVar = $outVar[$i].Line.Split("(")[0]
$testIn = $inVar[$i].Line.Split("(")[0]
Write-Host $testVar + $testIn
}
You can add the results into arrays then output them in the desired format. Like the following
#Empty arrays
$testIn_arr = #()
$testVar_arr = #()
foreach ($out in $outVar) {
$testVar = $out.Line.Split("(")[0]
$testVar_arr += #($testVar)
}
foreach ($in in $inVar) {
$testIn = $in.Line.Split("(")[0]
$testIn_arr += #($testIn)
}
for ($i = 0; $i -lt $testIn_arr.Count; $i++)
{
Write-Host $($testVar_arr[$i]) + "+" + $($testIn_arr[$i])
}

$line.StartsWith on multiple options

I want to be able to scan for two separate values in a line how do I do this?
$file = Get-Content "D:\path\tmp\certlist.txt"
foreach ($line in $file)
{
if ($line.StartsWith("snl") or $line.StartsWith("HSM1"))
{
$baseKey = "app.swift.snl."
$profileName = $line.Substring(0,13).TrimEnd()
$certType = $line.Substring(29,10).TrimEnd()
$renewalDate = Get_Unixtime $line.Substring(42,11).TrimEnd()
$expiryDate = Get_Unixtime $line.Substring(58,11).TrimEnd()
Send_Zabbix ($baseKey + $profileName + "." + $certType + ".renewaldate") $renewalDate
Send_Zabbix ($baseKey + $profileName + "." + $certType + ".expirydate") $expiryDate
}
}
The above doesn't work. It gives me an unexpected token.
For checking if a variable starts with one of several values a regular expression might be a better approach than daisy-chained StartsWith() calls.
if ($line -cmatch '^(snl|HSM1)')
{
...
}
^ matches the beginning of a string, (snl|HSM1) matches either snl or HSM1. -cmatch does a case-sensitive match. If you don't need case-sensitivity use -imatch or just -match.
Apparently all I needed was a '-'
if ($line.StartsWith("snl") -or $line.StartsWith("HSM1"))

Find the latest string based on time and display those

I have a text file as shown below:
testdatabase-21-07-15-12-00
testdatabase-21-07-15-18-00
testdatabase-21-07-15-23-00
testdatabase-22-07-15-12-00
testdatabase-22-07-15-18-00
testdatabase-22-07-15-23-00
and many more like this (dynamically generated)
I am comparing (21/22-07-15) with another text file and if a match is found, I need to see which is the latest one. Like, if match is found for date 21-07-15, I need to retrieve the latest (which is 23) from the many of 21. Same as the case for 22,.... if match is found.
What I have done so far is:
$temp = Get-Content "C:\RDS\temp.txt"
foreach($te in $temp)
{
$t = $te -split '-'
$da = $t[1]
$mo = $t[2]
$yea = $t[3]
if("$da-$mo-$yea" -match $temp1)
{
# need to write the concept here
}else
{
#nothing
}
}
How can I get this done.? Any help would be really appreciated.
You can sort the lines read from the input file by a calculated property:
$fmt = 'dd-MM-yy-HH-mm'
$culture = [Globalization.CultureInfo]::InvariantCulture
Get-Content "C:\RDS\temp.txt" |
sort { [DateTime]::ParseExact(($_ -split '-', 2)[1], $fmt, $culture) } |
select -Last 1