Why Am I geting an Empty Pipeline Error in this Script - powershell

I'm an extremely novice Powershell student who was given the task of getting the following code to work and I keep getting an Empty Pipeline Error at the line remarked 'Gives Empty Pipeline Error'. After quite a few hours of researching this I am still stumped as to what is causing this. The script is supposed to search the Application.evtx log and return any errors from the last 24 hours. I would greatly appreciate any help that could get me pointed in the right direction.
Here's the code:
#look for Errors script
#Creates Function named CheckLogs
Function CheckLogs()
{
# Defines a named parameter $logfile as a string
param ([string]$logfile)
if(!$logfile) {write-host "Usage: ""C:\Windows\System32\winevt\Logs\Application.evtx"""; exit}
# Accesses the file stored in $logfile variable and looks for the string "ERROR"
cat $logfile | Select-string "ERROR" -SimpleMatch |select -expand line |
foreach
{
$_ -match '(.+)\s\[(ERROR)\]\S(.+)'| Out-Null
new-object psobject -Property#{Timestamp=[datetime]$matches[1];Error=$matches[2]}
| #Gives Empty Pipeline Error
where {$_.timestamp -gt (get-date).AddDays(-1)}
$error_time=[datetime]($matches[1])
if ($error_time -gt (Get-Date).AddDays(-1))
{
write-output "CRITICAL: There is an error in the log file $logfile around
$($error_time.ToShortTimeString( ))"; exit(2)
}
}
write-output "OK: There were no errors in the past 24 hours."
}
CheckLogs "C:\Windows\System32\winevt\Logs\Application.evtx" #Function Call

You can't put the pipe | character on a line by itself. You can end a line with | and then continue the pipeline on the next line though.
This should work:
new-object psobject -Property#{Timestamp=[datetime]$matches[1];Error=$matches[2]} |
where {$_.timestamp -gt (get-date).AddDays(-1)}

Related

prepending a timestamp for note taking that outputs to a text file

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!')

Powershell - replace line in .txt if condition is met

i'm quite new to scripting (few weeks) and would be happy about your help.
I've a log-file (.txt) which needs to be changed.
The content is always the same:
random text
random text
successfull
error
random text
random text
random text
error
...
I would like to remove the line containing the word "error", but only if the line above contains the word "successfull".
So far I managed to get all the matching strings out of the File and am able to replace them, but I lose the rest of the text in the process:
get-content "D:\test.txt" | select-string -pattern "error" -context 1,0 | Where-Object {"$_" -match "successfull" } | %{$_ -replace "error.*"} | Out-File "D:\result.txt"
I would really appreciate your help here.
You can use some conditional logic (if statements) to achieve the goal:
$successful = $false
Get-Content d:\test.txt | Foreach-Object {
if ($_ -match "successfull") {
$successful = $true
$_
}
elseif ($_ -match "error" -and $successful) {
$successful = $false
}
else {
$_
$successful = $false
}
}
Since we are piping the Get-Content result into Foreach-Object, $_ becomes the current line being processed (each line is processed one by one). If a line contains successfull, then we mark $successful as $true and still output that line ($_). If the line contains error, then we will only output it if $successful is $false. Anytime we reach a line that does not contain succcesfull, we mark $successful as $false.
No deletion is actually occurring as it is merely not displaying error lines when the conditions are met.

Powershell issue with do while loop

I've got a simple bit of code that looks for a string in a series of log files.
If it finds the string, it should exit the loop (nested inside another loop as part of a function) with $buildlogsuccess = 'True'
If it can't find the string, it should exit and return $buildlogsuccess = 'False'
The select-string statement itself works, however it looks like there's something wrong with the below code:
$logArr = gci C:\build\Logs | where {($_.name -like 'install*.log') -and (! $_.PSIsContainer)} | select -expand FullName
$count = ($logArr).count
Foreach ($log in $logArr) {
Do {
$count -= 1
$buildlogsuccess = [bool](select-string -path $log -simplematch $buildstring)
If (($buildlogsuccess)) {break}
} while ($count -gt '0')
}
When one of the logs has the string, the loop finishes and should return $buildlogsuccess as 'True'.
If I check $log it shows the file that I know has the string (in this instance C:\build\Logs\Installer1.log).
Strangely, at this point $count shows as having a value of -1?
If I take the string out of that file and run again it also exits and returns the correct variable value (and shows the $log variable as the last file in $logArr as expected), but this time $count shows as -24.
My code is also returning $buildlogsuccess as 'False' when the string is present in one of the log files.
Re-tested [bool](select-string -path $log -simplematch $buildstring) by manually populating $log (with a file that has that string) and $buildstring and get 'True' as expected when using
[bool](select-string -path $log -simplematch $buildstring)
Note: Variables it uses:
$buildstring = "Package
'F:\xxx\Bootstrap\apackage\Installsomething.xml' processed
successfully"
Any help identifying where I've gone wrong would be appreciated.
Your code can be greatly simplified:
$buildlogsuccess = Select-String -SimpleMatch -Quiet $buildstring C:\build\Logs\install*.log
The above assumes that there are no directories that match install*.log; if there's a chance of that, pipe the output of Get-ChildItem -File C:\build\Logs -Filter install*.log to Select-String instead.
Do-while will first do the thing, then check the while statement. You're iterating over n files. It doesn't check the value of $count before it executes that portion.
So let's say the first file does not contain the string you're looking for. It will (correctly) decrement the $count variable to zero, and then it moves on to the next $log in $logArr.
Now for each next file in the folder it will decrement $count, and then exit the loop when it sees that $count is not greater than 0.
I don't know why you're using the do-while loop at all here
Thanks Norsk
I over-complicated for myself.
This worked:
$logArr = gci C:\build\Logs | where {($_.name -like 'install*.log') -and (! $_.PSIsContainer)} | select -expand FullName
$count = ($logArr).count
Foreach ($log in $logArr) {
$buildlogsuccess = [bool](select-string -path $log -simplematch $buildstring)
If ($buildlogsuccess) {break}
}

Check text file content in PowerShell

The PowerShell command
Get-ADFSRelyingPartyTrust | select Name | out-file C:\listOfNames.txt
generates a file as follows:
Name
----
AustriaRP
BahamasRP
BrazilRP
CanadaRP
[...]
Now, how can I check if BrazilRP has been extracted and C:\listOfNames.txt contains it?
The Get-Content and then Select-String should help. If the string is in the file it will get returned. If not then the command will returned empty value.
Get-Content C:\listOfNames.txt | Select-String "BrazilRP"
If the "BrazilRP" occurs more than once all the occurrences will returned so you know if you got any duplicates. Same holds if the string is a part of a longer expression. For example if you search for "zil" then "BrazilRP" will be returned as well.
Also you can pipe the results out to another file:
Get-Content C:\listOfNames.txt | Select-String "BrazilRP" | Out-File C:\myResults.txt
I found a solution (but thanks to PiotrWolkowski to suggest me the Get-Content function):
$file = Get-Content "C:\listOfNames.txt"
$containsWord = $file | %{$_ -match "BrazilRP"}
if ($containsWord -contains $true) {
Write-Host "There is!"
} else {
Write-Host "There ins't!"
}
If you want to easily see if a file contains your text try this
The [bool] type returns the data as either true or false instead of returning the actual data your searching for
if ([bool]((Get-Content -Path "C:\listOfNames.txt") -like '*BrazilRP*')) {
write-host "found it"
}
else {
write-host "didnt find it"
}

Get-Content - Get all Content, starting from a specific linenumber

My first question here, and just want to say thanks for all the input I've gotten over the years from this site.
I'm also new to powershell so the answar might be very simple.
I'm working on a Script that ment check a log file every 5 mins. (schedulded from ActiveBatch).
At the moment the script is searching for ERROR in a logfile. And it works fine.
But my problem is that the script searches the entire file throgh every time. So when an ERROR do occur, the check "fails" every 5 minutes the rest of the day. Untill a new logfile is generated.
My script:
Write-Host Opretter variabler...
$file = "${file}"
$errorString = "${errorString}"
Write-Host file variable is: $file
Write-Host errorString variable is: $errorString
Write-Host
Write-Host Select String Results:
$ssResult = Get-Content $file | Select-String $errorString -SimpleMatch
Write-Host
Write-Host There was $ssResult.Count `"$errorString`" statements found...
Write-Host
IF ($ssResult.Count -gt 0) {Exit $ssResult.Count}
So what i would like, is to Find the ERROR, and then Remeber the Linenumber (Perhaps in a file). Then in the next run (5minutes later) i want to start the search from that line.
for example. And error is found on line 142, the Script exits with error code 142. five minutes later the script is run again, and it should start from line 143, and go through the rest of the file.
You can remember number of error strings found in file:
$ssResult.Count > C:\path\to\file.txt
Then number of new erros is:
$errorCount = $ssResult.Count - (Get-Content C:\path\to\file.txt)
Remember to set the value in file to zero on first run of script and every time a new logfile is generated.
You basically gave a pretty good description of how it will work:
Read the last line number
$if (Test-Path $Env:TEMP\last-line-number.txt) {
[int]$LastLineNumber = #(Get-Content $Env:TEMP\last-line-number.txt)[0]
} else {
$LastLineNumber = 0
}
Read the file
$contents = Get-Content $file
Find the first error starting at $LastLineNumber (one of the rare cases where for is appropriate in PowerShell, lest we want to create nicer objects)
for ($i = $LastLineNumber; $i -lt $contents.Count; $i++) {
if ($contents[$i] -like "*$errorString*") {
$i + 1 > $Env:TEMP\last-line-number.txt
exit ($i + 1)
}
}
Select-String returns matchinfo objects, which have the line number, so you can should be able to do something like this:
$lasterror = Get-Content $lasterrorfile
$newerrors = select-string -Path $file -Pattern $errorstring -SimpleMatch |
where $_.LineNumber -gt $lasterror
Write-Host "$($newerrors.count) found."
if ($newerrors.count)
{$newerrors[-1].LineNumber | Set-Content $lasterrorfile}
So this is my final Script, Thanks Dano. I'm sure the Day-Reset thing can be done smarter, but this seems to work :)
#logic for Day-Reset
Write-Host checking if its a new day...
$today = Get-Date -format dddd
$yesterday = Get-Content $ENV:TEMP\${adapterName}_yesterday.txt
Write-Host today variable is: $today
Write-Host yesterday variable is: $yesterday
Write-Host
IF ($today.CompareTo($yesterday))
{
Get-Date -format dddd > $ENV:TEMP\${adapterName}_yesterday.txt
0 > $ENV:TEMP\${adapterName}_numberofErrors.txt
}
Write-Host Setting variables...
$file = "${file}"
$errorString = "${errorString}"
Write-Host file variable is: $file
Write-Host errorString variable is: $errorString
Write-Host
Write-Host Select String Results:
$ssResult = Get-Content $file | Select-String $errorString -SimpleMatch
Write-Host There was $ssResult.Count `"$errorString`" statements found...
$errorCount = $ssResult.Count - (Get-Content $ENV:TEMP\${adapterName}_numberofErrors.txt)
Write-Host There was $errorCount new `"$errorString`" statements found...
Write-Host
$ssResult.Count > $Env:TEMP\FXAll_numberofErrors.txt
Exit $errorCount