parsing text-files in powershell with where and select-string operators - optimization

recently I had to solve a problem reading a large amount of logfiles and picking specific chunks of text out of them.
With some trial and error I found a working solution but I'd like to know if there are better approaches.
These logfiles contain text-blocks, each is introduced by a 'heading' followed by an unknown amount of entries and finished with an empty line. (Example below, the numbers are pseudo linenumbers)
35# logevent1
36# entry1
37# entry2
38# entry3
39#
40# logevent2
41# entry1
42# entry2
Thus I know the 'logevent'-tag I can retrieve the line with $line = $logfile | Select-String -Pattern 'logevent1' and with $lineNumber = $line | Select-Object -ExpandProperty LineNumber I have the first value to use Get-Content with range operator [$x..$y].
In my example this would be 35. But how do I get the first empty line behind the text block?
I tried to work with Select-String -Pattern '' but that gives an instant exception due to the string is empty. So I wrote he following function:
function Get-TextBlock([string]$filePath,$lineNumber)
{
$startLine = ($lineNumber -1)
$counter = 0
$emptyLines = #()
Get-Content (Get-ChildItem $filePath) | ForEach-Object {
if( $_ -eq '' ) {
$emptyLines += $counter;
}
$counter++
}
$endLine = 0
$counter = 0
while( $endLine -le $startLine) {
$endLine = ($emptyLines[$counter]); $counter++;
}
$output += ((Get-ChildItem $filePath) | Get-Content)[$startLine..$endLine]
return $output
}
As mentioned before the function works for me but I feel like there are much better and easier ways to perfrom this task.
The Output (after removing my pseudo-line-numbers ;) ) looks like this
PS F:\scripts\powershell> Get-TextBlock '.\function-test.txt' 35
logevent1
entry1
entry2
entry3
____________________________________________________________________________
Kind regards

Related

Powershell IComparable errors when foreach looping through an array of SQL data

I'm creating an array with the following:
$QueryResults = QuerySQL -SQLInstance $Instance -SQLQuery $Query -SQLDatabase PI | select PercentDiff | format-table -HideTableHeaders
It contains:
0.36
0.47
0.89
The following foreach loop is throwing the below error:
if ($row -lt 1 -And $row -gt -1) {
$over1Percent = "False"}
else {
$over1Percent = "True"}
break
}
Cannot compare "Microsoft.PowerShell.Commands.Internal.Format.FormatStartData" because it is not IComparable.
I can't work out why this is occurring, would anyone be able to help me out? Thanks!!
The output from Format-Table is only good for one thing: presenting data to the user - it's absolutely useless for operating on the underlying data from which it was generated.
If you want just the 3 PercentDiff values, change your query pipeline to:
$QueryResults = QuerySQL -SQLInstance $Instance -SQLQuery $Query -SQLDatabase PI | Select -ExpandProperty PercentDiff

Powershell Add Spaces to Registry Data Output

I'm trying to create a small script, that can easily display some valid information to the standard user in regards of getting IT assistance from ServiceDesk.
Current output
So to improve this i was trying to figure out if i could add spaces to the teamviewer result.
This is an example of the current team viewer ID outcome:
1483547869
But i would like if the outcome could be:
1 483 547 869
This is a small thing but it will make it a lot easier to read for the standard user.
This is my code:
Add-Type -AssemblyName System.Windows.Forms
$ip=get-WmiObject Win32_NetworkAdapterConfiguration|Where {$_.Ipaddress.length -gt 1}
$ipaddress = $ip.ipaddress[0]
$hostname = [System.Net.Dns]::GetHostName()
$TeamViewerVersions = #('10','11','12','13','14','')
If([IntPtr]::Size -eq 4) {
$RegPath='HKLM:\SOFTWARE\TeamViewer'
} else {
$RegPath='HKLM:\SOFTWARE\Wow6432Node\TeamViewer'
}
$ErrorActionPreference= 'silentlycontinue'
foreach ($TeamViewerVersion in $TeamViewerVersions) {
If ((Get-Item -Path $RegPath$TeamViewerVersion).GetValue('ClientID') -ne $null) {
$TeamViewerID=(Get-Item -Path $RegPath$TeamViewerVersion).GetValue('ClientID')
}
}
$msgBoxInput = [System.Windows.Forms.MessageBox]::Show("Computer Name: $hostname`nIP Address: $ipaddress`nTeamViewer ID: $TeamviewerID`n`nWould you like to open Self Service Portal?", 'Quick Support','YesNo','Information')
If ($msgBoxInput -eq 'Yes' ){
start https://www.google.com/
Else
}
Stop-Process -Id $PID
Here's a really simple solution to format a number:
$TeamViewerDisplayID = $TeamViewerID.toString("### ### ### ###")
This will display 1483547869 as 1 483 547 869. Note: if your number will have 9 characters for example, the above line of code will add a space to the beginning. Example: "483547869" becomes "_483 547 869". So if you want, you can add another if statement there that checks how long the number is and formats it accordingly:
if ($TeamViewerID.length -gt 9) {
$TeamViewerID.toString("### ### ### ###")
} else {
$TeamViewerID.toString("### ### ###")
}

Powershell Code for List of Distinct Directories

I have a file that contains a list such as:
tables\mytable1.sql
tables\myTable2.sql
procedures\myProc1.sql
functions\myFunction1.sql
functions\myFunction2.sql
From this data (and there will always be a path, and it will always be only one level), I want to retrieve a list of distinct paths (e.g. tables\, procedures\, functions\)
To maybe make it the file that contains this data will already have been read into a list (named $fileList), so the new list ($directoryList ??) can likely derived from it.
I've found reference to the -unique parameter, but I need to look from the start of the line, up to (and including) the '\', of which there will only be one occurrence of).
Assuming you already have the data on $fileList, try this:
$directoryList = $fileList | %{ $_.split("\")[0]} | select -unique
It will do a foreach (the %{}) on the elements of your list, and then split them by the \ and get you only the first part (in your case, the folder name), after that you use select -unique to get just the distinct values.
Alternatively, you could do it like this:
$fileList | %{ $_ -replace "\\.*$","" } | select -unique
Using -replace to remove everything after the \.
Also, if for some reason you don't have the values of your textfile on $fileList already, you can do so using:
$fileList = Get-Content yourFile.txt
Your file may contain empty lines and more often than not there's an empty line on the last one so this will account for that.
It also has a slightly different regular expression to match from the end of the string that is not a \ character which will work for paths with multiple levels including your example.
If you have a text file with the following:
Z:\Path to somewhere\Files\some file 1.txt
Z:\Path to somewhere\Files\some file 2.txt
tables\mytable1.sql
tables\myTable2.sql
procedures\myProc1.sql
functions\myFunction1.sql
functions\myFunction2.sql
With this code which also shows the output after the function:
$fileListToProcess = "$([Environment]::GetFolderPath(""Desktop""))\list.txt"
Function Get-UniqueDirectoriesFromFile {
Param
(
[Parameter(Mandatory = $true, HelpMessage = 'The file where the list of files is.')]
[string]$LiteralPath
)
if (Test-Path -LiteralPath $LiteralPath -PathType Leaf) {
$fileList = [IO.File]::ReadAllLines($LiteralPath)
return $fileList | %{ $_ -replace '\\[^\\]*$', '' } | ? { $_.trim() -ne "" } | Select -Unique
}
else {
return $null
}
}
$uniqueDirs = Get-UniqueDirectoriesFromFile -file $fileListToProcess
# Display the results:
$uniqueDirs
# PS>
# Z:\Path to somewhere\Files
# tables
# procedures
# functions
$uniqueDirs.count
# PS> 4

How can I have a powershell loop that echos echo on the same line?

Currently I have the follow ps that reads a list of user names and then echos it.
The username file is the following
username1
username2
username3
The ps script is the following
$userNames = (Get-Content usernames.txt)# | Sort-Object
$userID=0
$userNames.Count
echo "FROM table WHERE (userID ='"
For ($i =1; $i -le ($userNames.Count - 1); $i++)
{
echo $userNames[$userID] "' OR userID='"
$userID++
}
echo $userNames[$userNames.Count - 1] "'"
I am hoping to get this to echo (and eventually write to a text file) all on the same line.
FROM table WHERE (userID = 'username1' OR userID = 'username2' OR userID = 'username3'
How would I go about solving this?
What you are looking for is:
Write-Host "Blah" -NoNewLine
I would probably re-write the script like this to avoid having to use the For...Loop
$userNames = (Get-Content usernames.txt) | Sort-Object
$count = 0
Write-Host "FROM table WHERE (" -NoNewLine
$userNames |% {
Write-Host "userID='$_'" -NoNewLine
if(++$count -ne $userNames.Length){
Write-Host " OR " -NoNewLine
}
else {
Write-Host ")"
}
}
This script will also take advantage of another nice feature of PowerShell, which is variable substitution in string literals. For-EachObject automatically sets $_ to be the current object during the iteration, and PowerShell will automatically parse variables in string literals and substitute their values.
Also... I just realized the entire thing can be reduced to the following:
$userNames = (Get-Content usernames.txt) | Sort-Object |% { "'$_'" }
Write-Host "FROM table WHERE UserID in ($([String]::Join(",",$userNames)))"
Which will produce the following query:
FROM table WHERE UserID in ('username1','username2','username3')
This is a much more pleasant script and query in my opinion :)

Read var directly from csv

Is it possible to read a variable directly by loading a csv?
My csv looks like this:
Var,Path
$SrcHost,\\computer
Is there a possibility to import-csv and put the path into the var?
import-csv test3.csv | foreach-object {
iex "$($_.var) = ""$($_.path)"""
}
You can also use new-variable (nv):
import-csv csvfile.csv | % { nv -name ($_.var) -value ($_.path) }
However to make this work you have to:
remove the $ from the source csv
or, trim $ as described by the comments below
or, select your variable as ${$srchost}
How bout this:
Get-Content -Path test.csv | Select-Object -Skip 1 | ForEach-Object { $_.Replace(",", "=`"") +
"`"" } | Invoke-Expression