Pipe to Out-GridView and Out-File got different rows? - powershell

The following command returns only one row (the parameter -Context 10 is ignored.)
select-string -path file.txt -pattern "..." -Context 10 | Out-GridView
However, the following command create a file with all the lines.
select-string -path file.txt -pattern "..." -Context 10 | Out-File file2
Why there is a difference?

This is because Out-Gridview consumes the entire MatchInfo object that Select-String outputs, and displays all of the properties of that object as columns. Out-File on the other hand basically performs the ToString() method on everything before it outputs it to a file, and for that kind of object when it converts to a string it outputs the line, and the context lines as well. If you want Out-GridView to do that you will have to pipe to Out-String and then to Out-Gridview.

Related

How do I find substring after using select-string in powershell?

How do I continue to get the 12345 as output?
file.txt
MyText: 12345
myscript
Get-Content file.txt | Select-String "MyText:" | Select-Object Line
Now I would like to save last five characters, how to I retreive them?
The Select-String cmdlet -Pattern parameter expects a regex - so you can get your desired output by capturing the five digits. Note that the Select-String cmdlet also takes a -Path parameter to retrieve the content of a file. This means you can omit the first Get-Content command:
(Select-String -Path .\file.txt -Pattern 'MyText: (\d{5})').Matches.Groups[1].Value

simple PowerShell Select-String Pattern

Why doesn't the PowerShell script
Get-NetRoute | Select-String -Pattern "255"
Get-NetRoute | Select-String -Pattern 255
Get-NetRoute | Select-String -Pattern '255'
Get-NetRoute | Select-String -Pattern '.*255.*'
give any result? Whats wrong with the Pattern?
The Select-String cmdlet is designed to work on string objects. The output of a Get-NetRoute cmdlet is an array.
For your code to work, you would have to convert it to a string object like this.
(Get-NetRoute | out-string).split("`n") | Select-String -Pattern "255"
But I doubt it would be very helpful if you intend to use the results down the lane.
What you really need is Where-object.
Get-NetRoute | Where-Object {$_.DestinationPrefix -like "*255*"}. Hope that helps.
Select-String will interpret Get-NetRoute as a single string (the output is a single object), so you need to split it up using the -Stream switch. Note that ipconfig | select-string would not have the same problem as it is a DOS command and so by default PowerShell will split it per line, but for PowerShell commands you need to do this when you want to do line by line interrogation since the pipeline is passing a single object, so you are doing it right to use Select-String, but just have to understand how it interprets input from the pipeline and then it'll work fine:
Get-NetRoute | Out-String -Stream | Select-String -Pattern "255"

Count Rows in CSV files and export results to CSV

I am trying count the rows containing values in a bunch of CSV in a folder. I managed to get the code to count it but I can't seem to find a way to export the results to a CSV. All I got is a blank CSV.
What am I missing here?
$FOLDER_ROOT = "C:\Test\2019"
$OUTPUT_CSV = "C:\Test\2019\Count.csv"
Get-ChildItem $FOLDER_ROOT -re -in "*.csv" | ForEach-Object {
$filestats = Get-Content $_.Fullname | Measure-Object -Line
$linesInFile = $filestats.Lines - 1
Write-Host "$_,$linesInFile"
} | Export-Csv -Path $OUTPUT_CSV -NoType
There are several issues with your code:
Use Get-ChildItem -Filter '*.csv' instead of Get-ChildItem -Include '*.csv'. The former is faster than the latter.
Write-Host most likely causes the output to go directly to the host console. I've been told that this was changed in recent versions (so that host output goes to the new information stream), but for versions at least prior to v5 it's still a reality.
Export-Csv expects object input, since it outputs the properties of the objects as the fields of the CSV (taking the column titles from the property names of the first object). Feeding it strings ("$_,$linesInFile") will result in a CSV that contains only a column "Length", since that is the only property of the string objects.
Use a calculated property for creating a CSV with the filename and line count of the input files:
Get-ChildItem $FOLDER_ROOT -Recurse -Filter '*.csv' |
Select-Object Name, #{n='LineCount';e={(Get-Content $_.Fullname | Measure-Object -Line).Lines - 1}} |
Export-Csv $OUTPUT_CSV -NoType
Write-Host writes only to the host! Most probably you see the output into the PowerShell Console?
Use Write-Output, which could be piped to Export-CSV.

How to Select-String multiline?

I am trying to Select-String on a text that is on multiple lines.
Example:
"This is line1
<Test>Testing Line 2</Test>
<Test>Testing Line 3</Test>"
I want to be able to select all 3 lines in Select-String. However, it is only selecting the first line when I do Select-String -Pattern "Line1". How can I extract all 3 lines together?
Select-String -Pattern "Line1"
Select-String operates on its input objects individually, and if you either pass a file path directly to it (via -Path or -LiteralPath) or you pipe output from Get-Content, matching is performed on each line.
Therefore, pass your input as a single, multiline string, which, if it comes from a file, is most easily achieved with Get-Content -Raw (PSv3+):
Get-Content -Raw file.txt | Select-String -Pattern Line1
Note that this means that if the pattern matches, the file's entire content is output, accessible via the output object's .Line property.
By contrast, if you want to retain per-line matching but also capture a fixed number of surrounding lines, use the -Context parameter.
Get-Content file.txt | Select-String -Pattern Line1 -Context 0, 2
Ansgar Wiechers' helpful answer shows how to extract all the lines from the result.
Select-String allows to select a given number of lines before or after the matching line via the parameter -Context. -Context 2,0 selects the preceding 2 lines, -Context 0,2 selects the subsequent 2 lines, -Context 2,2 selects the 2 lines before as well as the 2 lines after the match.
You won't get match and context lines in one big lump, though, so you need to combine matched line and context if you want them as a single string:
Select-String -Pattern 'Line1' -Context 0,2 | ForEach-Object {
$($_.Line; $_.Context.PostContext) | Out-String
}
As #mklement0 correctly pointed out in the comments, the above is comparatively slow, which isn't a problem if you're only processing a few matches, but becomes an issue if you need to process hundreds or thousands of matches. To improve performance you can merge the values into a single array and use the -join operator:
Select-String -Pattern 'Line1' -Context 0,2 | ForEach-Object {
(,$_.Line + $_.Context.PostContext) -join [Environment]::NewLine
}
Note that the two code snippets don't produce the exact same result, because Out-String appends a newline to each line including the last one, whereas -join only puts newlines between lines (not at the end of the last one). Each snippet can be modified to produce the same result as the other, though. Trim the strings from the first example to remove trailing newlines, or append another newline to the strings from the second one.
If you want the output as individual lines just output the Line and PostContext properties without merging them into one string:
Select-String -Pattern 'Line1' -Context 0,2 | ForEach-Object {
$_.Line
$_.Context.PostContext
}

Extract lines matching a pattern from all text files in a folder to a single output file

I am trying to extract each line starting with "%%" in all files in a folder and then copy those lines to a separate text file. Currently using this code in PowerShell code, but I am not getting any results.
$files = Get-ChildItem "folder" -Filter *.txt
foreach ($file in $files)
{
if ($_ -like "*%%*")
{
Set-Content "Output.txt"
}
}
I think that mklement0's suggestion to use Select-String is the way to go. Adding to his answer, you can pipe the output of Get-ChildItem into the Select-String so that the entire process becomes a Powershell one liner.
Something like this:
Get-ChildItem "folder" -Filter *.txt | Select-String -Pattern '^%%' | Select -ExpandProperty line | Set-Content "Output.txt"
The Select-String cmdlet offers a much simpler solution (PSv3+ syntax):
(Select-String -Path folder\*.txt -Pattern '^%%').Line | Set-Content Output.txt
Select-String accepts a filename/path pattern via its -Path parameter, so, in this simple case, there is no need for Get-ChildItem.
If, by contrast, you input file selection is recursive or uses more complex criteria, you can pipe Get-ChildItem's output to Select-String, as demonstrated in Dave Sexton's helpful answer.
Note that, according to the docs, Select-String by default assumes that the input files are UTF-8-encoded, but you can change that with the -Encoding parameter; also consider the output encoding discussed below.
Select-String's -Pattern parameter expects a regular expression rather than a wildcard expression.
^%% only matches literal %% at the start (^) of a line.
Select-String outputs [Microsoft.PowerShell.Commands.MatchInfo] objects that contain information about each match; each object's .Line property contains the full text of an input line that matched.
Set-Content Output.txt sends all matching lines to single output file Output.txt
Set-Content uses the system's legacy Windows codepage (an 8-bit single-byte encoding - even though the documentation mistakenly claims that ASCII files are produced).
If you want to control the output encoding explicitly, use the -Encoding parameter; e.g., ... | Set-Content Output.txt -Encoding Utf8.
By contrast, >, the output redirection operator always creates UTF-16LE files (an encoding PowerShell calls Unicode), as does Out-File by default (which can be changed with -Encoding).
Also note that > / Out-File apply PowerShell's default formatting to the input objects to obtain the string representation to write to the output file, whereas Set-Content treats the input as strings (calls .ToString() on input objects, if necessary). In the case at hand, since all input objects are already strings, there is no difference (except for the character encoding, potentially).
As for what you've tried:
$_ inside your foreach ($file in $files) refers to a file (a [System.IO.FileInfo] object), so you're effectively evaluating your wildcard expression *%%* against the input file's name rather than its contents.
Aside from that, wildcard pattern *%%* will match %% anywhere in the input string, not just at its start (you'd have to use %%* instead).
The Set-Content "Output.txt" call is missing input, because it is not part of a pipeline and, in the absence of pipeline input, no -Value argument was passed.
Even if you did provide input, however, output file Output.txt would get rewritten as a whole in each iteration of your foreach loop.
First you have to use
Get-Content
in order to get the content of the file. Then you do the string match and based on that you again set the content back to the file. Use get-content and put another loop inside the foreach to iterate all the lines in the file.
I hope this logic helps you
ls *.txt | %{
$f = $_
gc $f.fullname | {
if($_.StartWith("%%") -eq 1){
$_ >> Output.txt
}#end if
}#end gc
}#end ls
Alias
ls - Get-ChildItem
gc - Get-Content
% - ForEach
$_ - Iterator variable for loop
>> - Redirection construct
# - Comment
http://ss64.com/ps/