I've got a powershell script that monitors a log file, filters out the interesting bits and then presents those bits to me as and when they are written to the file. Works wonderfully. The line of interest is:
get-content "$logFile" -wait | where { select-string $searchTerm -inp $_ }
Now I want to get fancy!
I would like the font colour to change everytime a particular term is encountered. I can set the font colour easily enough, but how would you do it on-the-fly with the above statement?
Edit: Figured it out, but can't post an answer for 8 hours. Will upload it tomorrow.
If you're looking for something that provides selective color coding, then try something like this.
First, set up a helper function to select an appropriate color:
function Get-LogColor {
Param([Parameter(Position=0)]
[String]$LogEntry)
process {
if ($LogEntry.Contains("DEBUG")) {Return "Green"}
elseif ($LogEntry.Contains("WARN")) {Return "Yellow"}
elseif ($LogEntry.Contains("ERROR")) {Return "Red"}
else {Return "White"}
}
}
Then execute a line that looks like this:
gc -wait $logFile | ForEach {Write-Host -ForegroundColor (Get-LogColor $_) $_}
Try
Get-Content $logFile -Wait |
Select-String $searchTerm |
ForEach {write-host -ForegroundColor red $_.line}
Related
Trying to make a note-taking script that gives a timestamp and takes a note then prints it to a file. This script should also not exit until it reads wq! from the last line kinda like vim. I'm having trouble getting the timestamp in the right place. Here is what I have so far. Eventually, I'll get it so that wq! doesn't write or erases when complete... thanks for the help and time of anyone who responds. I prefer PowerShell but don't mind Python.
function Get-TimeStamp {
$timenow = get-date
return get-date -f $TimeNow.ToUniversalTime().ToString("HH:mm'Z'") #prints UTC timestamp
}
do
{
#write-output "$(Get-TimeStamp)" | C:\users\vider\Temp\Notes.txt ??? Maybe indexing?
#Hashtable? But how to get the time when the note is taken...
read-host "Notes" | out-file -FilePath C:\users\vider\Temp\Notes.txt -Append
Start-sleep -milliseconds 250
$choice = Get-Item -Path C:\users\vider\Temp\Notes.txt | Get-Content -Tail 1
} until ($choice -eq 'wq!')
Store the input from Read-Host in variable before outputting it to a file, this way you can add conditional statements to understand if the script should exit the loop or output to the file.
As for getting the timestamp in the right place, capturing the input in a variable as mentioned above, would also make it easier to concatenate the timestamp:
"[$(Get-TimeStamp)] $notes"
See Subexpression operator $( ) for more info on why it is needed in this case.
Here is one example on how you can do it:
function Get-TimeStamp {
(Get-Date).ToUniversalTime().ToString("HH:mm'Z'")
}
do
{
$notes = Read-Host "Notes"
if([string]::IsNullOrWhiteSpace($notes) -or $notes -eq 'wq!') {
# if this was just pressing ENTER or spaces or `wq!`, go to `until`
continue
}
# if we are here we can assume the input was NOT `wq!` or $null \ empty space
"[$(Get-TimeStamp)] $notes" | Out-File -FilePath C:\users\vider\Temp\Notes.txt -Append
Start-sleep -Milliseconds 250
} until ($notes -eq 'wq!')
I'm completely new to Powershell and trying to accomplish a small task. I'm trying to find a string in the last two lines of a log file, and if the value doesn't match, take and action. If it matches, end. I think that's the simplest way to explain it. :-) I'm searching for certain words, if not found, open notepad, if found, exit. I've pieced some of it together, but I'm not sure how to handle the If False, run notepad portion. Would love some help. Thanks.
if (Test-Path C:\windows\ccm\logs\CcmEval.log) {
Get-Content 'C:\windows\ccm\logs\CcmEval.log' -Tail 2 | Select-String "Updating MDM_ConfigSetting.ClientHealthStatus with value 7" | % { $_ -notmatch "value 7" }
Start-Process C:\windows\notepad.exe
}
You can do that with that with one more if else condition. If condition not matches it will open notepad otherwise it will execute your command to exit.
if (Test-Path C:\windows\ccm\logs\CcmEval.log) {
$condition = Get-Content 'C:\windows\ccm\logs\CcmEval.log' -Tail 2 | Select-String "Updating MDM_ConfigSetting.ClientHealthStatus with value 7" | % { $_ -notmatch "value 7" }
if (!$condition) {
Start-Process C:\windows\notepad.exe
}
else {
"Do something here"
}
}
You can drop the Test-Path if you add -ErrorAction SilentlyContinue to the Get-Content, so it will quietly return nothing if the file is not found. This means you can use the same code whether or not the file exists.
-match takes a regular expression pattern, which is powerful enough that you can build "not 7" into it in one go using [^7] to mean "any character except 7".
$logFile = 'C:\windows\ccm\logs\CcmEval.log'
$lines = Get-Content -Path $logFile -Tail 2 -ErrorAction SilentlyContinue
if ($lines -match "Updating MDM_ConfigSetting.ClientHealthStatus with value [^7]") {
Start-Process C:\windows\notepad.exe
}
I am attempting to highlight a single output field in my Powershell commandlet with some color. Seems simple and perhaps the answer is it cannot be done in a simple fashion. If that is the case, that is fine, just wanted to see if anyone knew of any easy way to accomplish the desired output.
What I am trying to run (Using Hashtable to attempt to add color):
Get-Mailbox -PublicFolder | Sort Name | FL #{Name='Mailbox Name';Expression={write-host -NoNewline $_.Name -ForegroundColor Green}},Proh*,Issue*,MaxReceiveSize,MaxSendSize,UseDatabaseQ*,Database
In the output you will notice the write-host is putting the name of the mailbox before it's label.
Code with Hashtable
Now to contrast, if I avoid attempting to add color (Simplified code without the Hashtable):
Get-Mailbox -PublicFolder | Sort Name | FL Name,Proh*,Issue*,MaxReceiveSize,MaxSendSize,UseDatabaseQ*,Database
Everything comes out perfectly formatted.
Simplified code without Hashtable
Is there a way to get the standard FL formatting but adding color to that one output?
Responses:
To Theo, Thank you that worked! Appreciate you adding the multi color option.
To JS2010, Thank you for your suggestion. I am, unfortuntely, not getting the desired output. As stated below, I am on PS 4.0 currently(plans to upgrade soon).
To Øyvind, similar to the issue seen in JS2010s code. Here is what I am getting from your suggestion. Possibly again a versioning issue.
You could do this by first capturing the Format-List output as string in a variable, split it at the newlines and output each line yourself like this:
$result = Get-Mailbox -PublicFolder | Sort-Object Name |
Format-List Name,Proh*,Issue*,MaxReceiveSize,MaxSendSize,UseDatabaseQ*,Database |
Out-String
If you only want to color the one property:
switch -regex ($result -split '\r?\n') {
'^Name\s*:' {
$label,$value = $_ -split ':',2
Write-Host ($label + ":") -NoNewline
Write-Host $value -ForegroundColor Green
}
default { $_ }
}
Output:
If you want to be able to color more properties, create a hashtable with the property names and the colors to use:
$propertyColors = #{
Name = 'Green'
MaxSendSize = 'Cyan'
}
switch ($result -split '\r?\n') {
default {
if (![string]::IsNullOrWhiteSpace($_)) {
$label,$value = $_ -split ':',2
if ($propertyColors.ContainsKey($label.Trim())) {
Write-Host ($label + ":") -NoNewline
Write-Host $value -ForegroundColor $propertyColors[$label.Trim()]
}
else { $_ }
}
else { $_ }
}
}
Output:
js2010's tip and variable interpolation will do the trick!
$e = [char]0x1b # escape
Get-Mailbox -PublicFolder | Sort Name | FL #{Name='Mailbox Name';Expression={"$e[32m$($_.Name)$e[0m"}},Proh*,Issue*,MaxReceiveSize,MaxSendSize,UseDatabaseQ*,Database
My first attempt. If you want to go through the trouble. There's probably a powershell module that does color (Pansies?). Write-host doesn't write to standard output, and redirection takes away the color.
$e = [char]0x1b # escape
[pscustomobject]#{a = "$e[32mhi$e[0m"} | format-table
Output (pretend the 'hi' is green):
a
-
hi
I have a piece of PS code which takes the 7-Zip extraction output and filters it down so only percentage "%" progress update lines get printed. I've managed to reduce it down to just the percentage outputs:
& $7ZipPath "x" $filePath "-o$extractionPath" "-aos" "-bsp1" | out-string -stream | Select-String -Pattern "\d{1,3}%" -AllMatches | ForEach-Object { $_.Matches.Value } | Write-Host -NoNewLine
At the moment the console output looks like this:
0%1%5%9%14%17%20%23%26%31%37%43%46%48%50%52%54%56%59%61%63%65%67%70%72%74%76%78%80%81%82%83%85%86%87%89%90%91%92%94%95%96%97%98%99%
Is there a way of keeping these outputs in the same place, on the same line, making them just overwrite each other? It's tricky because the output is being piped from the 7-Zip application. I'm afraid I can't use Expand-Archive as I am dealing with .7z files
Many thanks!
You could use the .Net System.Console class:
[System.Console]::SetCursorPosition(0, [System.Console]::CursorTop)
So your code would have to be:
& $7ZipPath "x" $filePath "-o$extractionPath" "-aos" "-bsp1" | out-string -stream | Select-String -Pattern "\d{1,3}%" -AllMatches | ForEach-Object { $_.Matches.Value } | foreach {
[System.Console]::SetCursorPosition(0, [System.Console]::CursorTop)
Write-Host $_ -NoNewLine
}
Note: As long as the next output is equal or greater length, which is true in your case, this is all you need. Otherwise you would have to clear the last output first.
marsze's helpful answer works well, but there's a simpler alternative that uses a CR character ("`r") to reset the cursor position to the start of the line.
Here's a simple demonstration that prints the numbers 1 through 10 on the same line:
1..10 | ForEach-Object { Write-Host -NoNewline "`r$_"; Start-Sleep -Milliseconds 100 }
[Console]::Write(...) instead of Write-Host -NoNewline ... works too, as Bacon Bits points out.
The same constraint applies, however: if previous output lines happened to be longer, the extra characters linger.
To solve this problem too, you must pad any output line to the length of the console window's buffer width:
'loooooooong', 'meeedium', 'short' | ForEach-Object {
Write-Host -NoNewline ("`r{0,-$([console]::BufferWidth)}" -f $_)
Start-Sleep -Milliseconds 500
}
I'm having some issues getting some info to write to the console before Read-Host. Let me throw out a simplified example.
Function Add-Build {
[CmdletBinding()]
Param ([Parameter(Mandatory=$True,Position=1)][String]$Build
,[Parameter(Mandatory=$False,Position=2)][System.Nullable``1[[System.Int32]]]$VersionID
,[Parameter(Mandatory=$False,Position=3)][String]$BuildDescription
)
Write-Host -BackgroundColor DarkYellow "Adding SQL Build $($Build)"
IF ($VersionID -eq $null)
{
Get-SqlVersions | Out-String
$VersionID = Read-Host -Prompt "SELECT Version (Enter To Skip)" | % { IF ($_ -eq '') {$null} ELSE {$_}}
}
}
FUNCTION Test-Function {
$BuildID = (Get-BuildID -Build "11.0.3156.0").ToString()
}
If I call Add-Build directly then the Get-SqlVersions | Out-String output before the Read-Host. If I call Test-Function though the Get-SqlVersions no longer outputs to the console at all. Get-SqlVersions makes a SQL proc call and the output is a couple Datarows.
Is there a way to ensure the Get-SqlVersions data shows up when calling Test-Function?
Make it explicitly output to the host.
$GetSQL = Get-SqlVersions | Out-String
Write-Host $GetSQL
Could you please store Get-SqlVersions | Out-String; in a Variable and display that. I think that should work.
$versions = Get-SqlVersions | Out-String;
$versions
I know this is old, but I stumbled on it and can't help but contribute.
The problem is here:
Get-SqlVersions | Out-String
Change it to this:
Get-SqlVersions | Out-Host
I did some quick looking around, and Out-String seems to collect and prepare things for displaying. Out-Host just does it.