Get-Content issue with -eq as string - powershell

As part of a bigger script I need to verify a file contents before continuing. However, it's not working when I use | Out-String.
Note this needs to work under powershell v2 unfortunately. The file I am looking at contains the data:
{"somedata":5,"hello":[]}
If I remove | Out-String from the command then it tells me the file matches.
But if I add data to the file, then it still tells me the file matches when it doesn't. If I add | Out-String then it tells me the file doesn't match when it does...
$filecheck = Get-Content ("C:\temp\hello.txt") | Out-String
Write-Host $filecheck
if ($filecheck -eq '{"somedata":5,"hello":[]}') {
Write-Host "file matches"
} else {
Write-Host "doesn't match"
}

As how to fix the issue, see #tukan's answer. Anyway, for learning purposes, let's explore the root cause, namely using the Out-String cmdlet. It actually adds a newline to the string. Like so,
PS C:\temp> $filecheck = Get-Content ("C:\temp\hello.txt") | Out-String
PS C:\temp> write-host $filecheck
{"somedata":5,"hello":[]}
PS C:\temp>
As the data contains a newline, it isn't equal to the string literal used in the if statement. Thus the comparison fails. Remove the Out-String and it works:
PS C:\temp>$filecheck = Get-Content ("C:\temp\hello.txt")
PS C:\temp> $filecheck
{"somedata":5,"hello":[]}
PS C:\temp> $filecheck -eq '{"somedata":5,"hello":[]}'
True
PS C:\temp>
Earlier you noted that Out-String was needed as otherwise adding data would still make the comparison to fail. Why is that? Let's say the data in file is
{"somedata":5,"hello":[]}
{"moredata":1,"foo":bar}
What now happens is that Get-Content will give you an array of strings. The 2nd line consists of {"moredata":1,"foo":bar} plus a newline. Passing such a construct to the comparison will evaluate just the first element, thus a match.
When you pass an array to Out-String, the result is actually a string with data, newline, data and extra newline:
PS C:\temp> $filecheck|out-string
{"somedata":5,"hello":[]}
{"moredata":1,"foo":bar}
PS C:\temp>
This obviously isn't equal to the string literal used in the if statement.

Edit - the script will exit on first match.
Edit2 - you don't need the Else branch if nothing is found print 'failed' at the end suffice.
What about piping into Foreach-Object like this:
(Get-Content 'C:\<our_path>\test.txt') |
Foreach-Object { If ($_ -eq '{"somedata":5,"hello":[]}') {write-host 'matches'; exit}}
write-host 'failed'
I have kept your format of -eq, even thou, I would recommend regex for string/text search.

Related

Powershell: how to easily switch between 'Out-String' and 'Get-Content' string variable types?

I want to use the method given in the answer of this question:
PowerShell - Remove all lines of text file until a certain string is found
However I don't get my string from "Get-Content"; I get it from "Out-String". How can I convert my "Out-String" variable into a "Get-Content" format without needing to "Set-Content"/"Get-Content" a temporary file? Or how can I get the same end result without even converting?
It really hurts my brains that a "Get-Member" on the variable from either 'Out-String' or 'Get-Content' returns a TypeName of System.String but you cannot use them the same way...
Here is the simplified code I've been trying to understand - let's use that:
# Let's work with the text from 'Get-Help' output:
$myString = (Get-Help | out-string)
# I only want the text from the "SEE ALSO" section:
$cut = $myString.Where({ $_ -like ("*SEE ALSO*") },'SkipUntil')
$cut # This shows the whole thing!!! :-(
$cut | gm | findstr TypeName # says 'TypeName: System.String'
# Dirty conversion to "Get-Content" format:
Set-Content "tmp.file" -value $cut
$recut = (Get-Content "tmp.file").Where({ $_ -like ("*SEE ALSO*") },'SkipUntil')
$recut # Now this shows what I want, even though the below returns 'TypeName: System.String' as well !!!
(Get-Content "tmp.file") | gm | findstr TypeName
The problem is get-help (with no parameters) or out-string is outputting one multiline string (with windows line endings). I even tried out-string -stream. This is unusual for a powershell command. Get-content would split up the lines for you automatically.
(get-help).count
1
One way to resolve it is to split on the line endings. I'm also skipping blank lines at the end. (This split pattern works with unix/osx text too.)
((get-help) -split '\r?\n').Where({ $_ -like '*SEE ALSO*' },'SkipUntil') | where { $_ }
SEE ALSO:
about_Updatable_Help
Get-Help
Save-Help
Update-Help
Or:
((get-help) -split '\r?\n').Where({ $_ -match 'SEE ALSO' },'SkipUntil').Where{ $_ }
In this case, you do not even need Out-String, but I will stick to your example:
$myString = (Get-Help | Out-String)
$mystring -match "(?ms)^.*(SEE\sALSO.*)$" | Out-Null
$Matches[1]
The key in the regex is (?ms). m enables multi-line search and s enables wildcards to span over multiple lines (in other words: including line breaks). The result of the -match operator is piped to Out-Null to not see it in the terminal. You might want to evaluate it though. If $true, $Matches[1] will contain your desired string.

Extract word from powershell output Search-String

Hi All I'm trying to extract a word from a text output. It should be pretty easy but I've already spent so much time on it. Right now I can extract the line but not just the word.
For example
w32tm /query /status | Select-String -pattern "CMOS"
outputs the line
"Source: Local CMOS Clock"
I only want to extract "Local CMOS Clock"
$var1=w32tm /query /status | Select-String -pattern "CMOS"
$var2=($var1 -split ':')[1] | Out-String
I was able to come up with the above it seems to work I'm not sure if there's a better way, I'm trying to evaluate it through a true/false seem to always pass as true though
For example
if($var2 = "Local CMOS Clock"){
Write-Output "True";
}Else{
Write-Output "False";
}
Always true: even when the condition is wrong
thanks in advance.
I'm not entirely sure of your motives, but here's a cleaner way to get to the answer you're looking for:
Build a PSObject containing the output
The PSObject will contain the output of w32tm. The code works by piping the command output through a loop, at the beginning we make a HashTable and then this is used to build a PowerShell object which is easier to manipulate:
# Pipe the w32tm command through a foreach
# Build a hashtable object containing the keys
# $_ represents each entry in the command output, which is then split by ':'
$w32_obj = w32tm /query /status | ForEach-Object -Begin {$w32_dict = #{}} -Process {
# Ignore blank entries
if ($_ -ne '') {
$fields = $_ -split ': '
# This part sets the elements of the w32_dict.
# Some rows contain more than one colon,
# so we combine all (except 0) the split output strings together using 'join'
$w32_dict[$fields[0]] = $($fields[1..$($fields.Count)] -join ':').Trim()
}
} -End {New-Object psobject -Property $w32_dict}
View the PSObject
Simply run this to display the new PSObject that has been created:
$w32_obj
Now check the 'Source'
Now we can ask for the 'Source' object from $w32_obj by using the dot-notation: $w32_obj.Source:
if($w32_obj.Source -eq "Local CMOS Clock"){
Write-Output "True";
}Else{
Write-Output "False";
}
Further reading
This shows the conversion from HashTable to PSobject and vice-versa
PSCustomObject to Hashtable
here's yet another way to get the False/True from w32tm. my system does not have "cmos" in the output, so i use 'system clock', but the idea will work for your situation.
[bool]((w32tm /query /status) -match 'system clock')
the above returns a $True on my system. that seems a tad more direct than the method you used. [grin]
take care,
lee

Overwrite PowerShell output strings onto the same line

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
}

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"
}

PowerShell query: how to process a file

I often want to process a file resulting in a modified output file. I can't seem to find the PowerShell way to do this - it always ends up looking like code in a one-liner, IYSWIM. This is an example.
I have an LMHOSTS file. Don't ask why! I want to get a new LMHOSTS file that only contains servers that respond to pings. I also want to pass through any comment lines.
Comment lines begin with a #.
Data lines are something like this (space-separated):
10.123.177.1 SVRDS1 #PRE #DOM:DOM1
or this:
10.241.177.30 SVRDS30 #PRE
This is what I've got (with thanks to Luke for help with Ping-Host):
gc 'C:\work\lmhosts.txt' | % { if ($_ -like '#*') { $_ | out-file 'C:\work\lmhosts2.txt' -append } else { if ($(Ping-Host $_.Substring(0, $_.indexof(' ')) -count 1 -timeout 10).received -eq 1) { $_ | out-file 'C:\work\lmhosts2.txt' -append } } }
It works but it's not nice. I've taken a programmer's approach but done it as a one-liner. Is there a better way of doing it, that's more 'shell' and less 'code'?
In this particular example, you are filtering the contents of 1 file and outputting it to another. Where-Object is usually a good choice for this. You can then simplify your one-liner a bit:
gc 'C:\work\lmhosts.txt' |
?{ ($_ -like '#*') -or
($(Ping-Host $_.Split(' ')[0]) -count 1 -timeout 10).received -eq 1) } >
'C:\work\lmhosts2.txt'
Some notes:
"?" is an alias for Where-Object
The -or operator will short circuit, that is, if the first operand results in True, the second will not bother executing.
You can use the redirection operator ">" instead of "| Out-File".
I replaced Substring with Split, it seemed slightly simpler, not sure if works in general with your input.
Whitespace is your friend! In Powershell scripts I write that aren't immediately thrown away, almost every '|' is followed by a newline + indent.