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])
}
Related
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/ $_
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"))
I've got an array definition:
> $a={"abc","xyz","hello"}
Then, using foreach to modify it, but seems original elements are not changed:
> foreach($i in $a){$i="kkk"+$i}
> $a
> "abc","xyz","hello"
Why foreach loop doesn't modify elements?
Then I tried to use ForEach-Object, this time, it doesn't even run:
> $a|%{$_=$_+"kkk"}
Method invocation failed because [System.Management.Automation.ScriptBlock] does not contain a method named 'op_Addition'.
At line:1 char:6
+ $a|%{$_=$_+"kkk"}
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (op_Addition:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Does this has any syntax error? Or my understanding is incorrect?
You don't have an array in $a, you have a [ScriptBlock]. Curly braces {} denote a script block in PowerShell.
The array operator is #().
This is the cause of the error in the second example.
$a=#("abc","xyz","hello")
foreach($i in $a) {
$i = "zzz" + $i
}
$a | % { $_ += "zzz" }
However, this still will not work, because $i and $_ are copies, not references back to the original array location.
Instead, you can iterate over the array using a regular for loop:
for( $i = 0 ; $i -lt $a.Count ; $i++ ) {
$a[$i] += "zzz"
}
In this example you can see that in the loop body, you are referring directly to the array in $a and modifying its actual value.
Also note that ForEach-Object (%) returns a value (or all of the values returned from the block), so you could also do this:
$a = $a | % { "qqq" + $_ }
However this is forming a brand new array and assigning it to $a, not really modifying the original.
I need to benchmark two different programs on two diferent corpuses, and in order to get more accurate readings, I want to run the benchmark in a loop and take the mean execution time for each benchmark. In order to simplify for myself, I wrote the following PowerShell function:
Function Benchmark {
Param($progPath, $benchmarkPath, $iters=27)
$time = (Measure-Command { & "$progPath" "$benchmarkPath" }).TotalSeconds
$sum = $lowest = $highest = $time
for($i = 1; $i -lt $iters; $i++) {
$time = (Measure-Command { & "$progPath" "$benchmarkPath" }).TotalSeconds
$sum += $time
if($time -lt $lowest) { $lowest = $time }
elseif($time -gt $highest) {$highest = $time }
}
$sum -= ($lowest + $highest)
$sum / ($iters - 2)
}
In theory, this should execute the program supplied as a command in $progPath with the benchmarking script in $benchmarkPath as its argument, but when I run it like this, I get the following result:
PS > $nonPrivateBenchmark = Benchmark(".\Python\PCbuild\amd64\python", ".\Benchmarks\non_private_access.py")
& : The term '.\Python\PCbuild\amd64\python .\Benchmarks\non_private_access.py' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:3 char:30
+ $time = (Measure-Command { & "$progPath" "$benchmarkPath" }).TotalSeconds
+ ~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (.\Python\PCbuil...ivate_access.py:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
(Plus 26 repetitions of the same error but on line 6.)
However, if assign the three parameter arguments and copy the remaining function body directly into PowerShell, it works and sets $nonPrivateAccess to a reasonable value:
$progPath = ".\Python\PCbuild\amd64\python"
$benchmarkPath = ".\Benchmarks\non_private_access.py"
$iters = 27
$time = (Measure-Command { & "$progPath" "$benchmarkPath" }).TotalSeconds
$sum = $lowest = $highest = $time
for($i = 1; $i -lt $iters; $i++) {
$time = (Measure-Command { & "$progPath" "$benchmarkPath" }).TotalSeconds
$sum += $time
if($time -lt $lowest) { $lowest = $time }
elseif($time -gt $highest) {$highest = $time }
}
$sum -= ($lowest + $highest)
$nonPrivateBenchmark = $sum / ($iters - 2)
I have through experimentation concluded that the problem is that "$progPath" "$benchmarkPath" is concatenated into the single string '.\Python\PCbuild\amd64\python .\Benchmarks\non_private_access.py' before executed with the & operator, and the space separating them is interpretted as a part of the command name, making PowerShell try to execute the entire string as a single command (which can't be executed). I have tried putting escaped quotes both around and inside the argument parameter, but to no avail. Does anyone else have a solution to this problem?
PS:
Quite extensive searching has only given me a lot of hits with people having the opposite problem. Could it be that I have some non-default PowerShell settings activated making it parse the spaces overly aggressively?
Benchmark(".\Python\PCbuild\amd64\python", ".\Benchmarks\non_private_access.py")
This syntax is passing an array to the first parameter of your Benchmark function, which then gets converted to a single string when it's used as a command. This is effectively the same as:
Benchmark ".\Python\PCbuild\amd64\python", ".\Benchmarks\non_private_access.py"
The normal syntax for passing multiple parameters to a PowerShell function is to place a space between the parameters:
Benchmark ".\Python\PCbuild\amd64\python" ".\Benchmarks\non_private_access.py"
You can also use the parameter names:
Benchmark -progPath ".\Python\PCbuild\amd64\python" -benchmarkPath ".\Benchmarks\non_private_access.py"
I am new to Powershell, first time using it.
I have declared an array and use array value but using below code, I am not able to retrieve the array value...Any idea what I am missing here?
Just FYI.. I am executing script in ADOBE ILLUSTRATOR and for testing I am using 3 here in condition (for loop)... will use $array later
$array = "a.jpg","b.jpg","c.jpg";
for ($i=1; $i-le=3; $i++)
{
$.writeln("This is line number " + $array[$i]);
var targetFileName = $array[$i]+'.png';
$.writeln(targetFileName);
}
I tried $array[$i].toString() as well but still not getting values... I am getting 0
Any help is appreciated and thanks in advance to all for your help
for ($i=1; $i-le=3; $i++)
The condition in the above line doesn't have a valid comparison operator. Change that to
for ($i=1; $i -le 3; $i++)
if you want the loop to terminate after 3 cycles.
$.writeln("This is line number " + $array[$i]);
var targetFileName = $array[$i]+'.png';
$.writeln(targetFileName);
This is not valid PowerShell. Looks more like JavaScript to me. In PowerShell it should probably look like this:
Write-Output "This is line number $i"
$targetFileName = $array[$i] + '.png'
Write-Output $targetFileName
or shorter
"This is line number $i"
$array[$i] + '.png'
Note that PowerShell arrays are zero-based, so the last iteration ($array[3]) will return $null instead of an element from the array. If you want to iterate over the elements of the array you should change your loop to this:
for ($i=0; $i -lt $array.Length; $i++) {
"This is line number $($i+1)"
$array[$i] + '.png'
}
or (better) pipe your array into a foreach loop:
$i = 0
$array | % {
"This is line number " + ++$i
$_ + '.png'
}