PowerShell equivalent to grep -f - powershell

I'm looking for the PowerShell equivalent to grep --file=filename. If you don't know grep, filename is a text file where each line has a regular expression pattern you want to match.
Maybe I'm missing something obvious, but Select-String doesn't seem to have this option.

The -Pattern parameter in Select-String supports an array of patterns. So the one you're looking for is:
Get-Content .\doc.txt | Select-String -Pattern (Get-Content .\regex.txt)
This searches through the textfile doc.txt by using every regex(one per line) in regex.txt

PS) new-alias grep findstr
PS) C:\WINDOWS> ls | grep -I -N exe
105:-a--- 2006-11-02 13:34 49680 twunk_16.exe
106:-a--- 2006-11-02 13:34 31232 twunk_32.exe
109:-a--- 2006-09-18 23:43 256192 winhelp.exe
110:-a--- 2006-11-02 10:45 9216 winhlp32.exe
PS) grep /?

I'm not familiar with grep but with Select-String you can do:
Get-ChildItem filename.txt | Select-String -Pattern <regexPattern>
You can also do that with Get-Content:
(Get-Content filename.txt) -match 'pattern'

I had the same issue trying to find text in files with powershell. I used the following - to stay as close to the Linux environment as possible.
Hopefully this helps somebody:
PowerShell:
PS) new-alias grep findstr
PS) ls -r *.txt | cat | grep "some random string"
Explanation:
ls - lists all files
-r - recursively (in all files and folders and subfolders)
*.txt - only .txt files
| - pipe the (ls) results to next command (cat)
cat - show contents of files comming from (ls)
| - pipe the (cat) results to next command (grep)
grep - search contents from (cat) for "some random string" (alias to findstr)
Yes, this works as well:
PS) ls -r *.txt | cat | findstr "some random string"

So I found a pretty good answer at this link:
https://www.thomasmaurer.ch/2011/03/powershell-search-for-string-or-grep-for-powershell/
But essentially it is:
Select-String -Path "C:\file\Path\*.txt" -Pattern "^Enter REGEX Here$"
This gives a directory file search (*or you can just specify a file) and a file-content search all in one line of PowerShell, very similar to grep. The output will be similar to:
doc.txt:31: Enter REGEX Here
HelloWorld.txt:13: Enter REGEX Here

I find out a possible method by "filter" and "alias" of PowerShell, when you want use grep in pipeline output(grep file should be similar):
first define a filter:
filter Filter-Object ([string]$pattern) {
Out-String -InputObject $_ -Stream | Select-String -Pattern "$pattern"
}
then define the alias:
New-Alias -Name grep -Value Filter-Object
final, put the former filter and alias in your profile:
$Home[My ]Documents\PowerShell\Microsoft.PowerShell_profile.ps1
Restart your PS, so you can use it:
alias | grep 'grep'
References
alias:
Set-Alias here
New-Alias here
Filter (Special function) here
Profiles (just like .bashrc for bash): here
out-string (this is the key) here:
in PowerShell Output is object-based here,so the key
is to convert object to string and grep the string.
Select-String here:
Finds text in strings and files

This question already has an answer, but I just want to add that in Windows there is Windows Subsystem for Linux WSL.
So for example if you want to check if you have service named Elasicsearch that is in status running you can do something like the snippet below in powershell
net start | grep Elasticsearch

but select-String doesn't seem to have this option.
Correct. PowerShell is not a clone of *nix shells' toolset.
However it is not hard to build something like it yourself:
$regexes = Get-Content RegexFile.txt |
Foreach-Object { new-object System.Text.RegularExpressions.Regex $_ }
$fileList | Get-Content | Where-Object {
foreach ($r in $regexes) {
if ($r.IsMatch($_)) {
$true
break
}
}
$false
}

Maybe?
[regex]$regex = (get-content <regex file> |
foreach {
'(?:{0})' -f $_
}) -join '|'
Get-Content <filespec> -ReadCount 10000 |
foreach {
if ($_ -match $regex)
{
$true
break
}
}

Related

Add index number to list of results of `Select-String`

I need to search for a term in a file and display an index in the search results so that referencing is easy.
The command in bash would be:
cat <file> | grep 'Table: ' | cat -n
What I have so far in Powershell:
Get-Content <file> | Select-String 'Table: ' | Select-Object -Property LineNumber, Line
Unfortunately, I didn't realize that LineNumber gives the actual line in the file and not the index in the results list.
How can I translate the bash command into its Powershell equivalent?
Indeed, the .Line property of the objects output by Select-Object indicates the line number of each match in a given input file.
PowerShell has no direct equivalent of cat -n (prepending a 1-based index to all input lines on output), but it's not hard to roll your own using the ForEach-Object cmdlet:
$i = 0
Get-Content file.txt | Select-String 'Table: ' | ForEach-Object {
"{0,6}`t{1}" -f ++$i, $_.Line
}
The above uses -f, the format operator, to left-space-pad to 6 characters (,6) the first RHS operand ({0}), which is the (incremented) index, ++$i, followed by a tab character (`t) and the second RHS operand ({1}), which is the input line at hand ($_.Line).
Just use WSL:
bash -c "cat <file> | grep 'Table: ' | cat -n"
This will run the bash code in powershell. For a true powershell option you could do this:
foreach ($line in $(Get-Content -Path <filepath> | Select-String 'Table: ')){
$count++
echo "$count $line"
}

How to replace the content of every file in the directory?

There is a function (Function($_)) that replace all "1" with "2" for each file in the directory. New content is written to the file out.txt.
input: in.txt → 111
output: in.txt → 222 → out.txt
Tell me, please, how to make the replacement take place inside of every file?
Get-Content "C:\Dir\*" | ForEach-Object {Function($_)} > C:\Dir\out.txt
Get-Content "C:\Dir\*" will give you the content of everything in C:\Dir in one go, so you won't be able to modify each file individually. You'll also get errors for any directory in C:\Dir.
You need to iterate over each file in the directory and process them individually:
Get-ChildItem 'C:\Dir' -File | ForEach-Object {
$file = $_.FullName
(Get-Content $file) -replace '1','2' | Set-Content $file
}
The parentheses around Get-Content ensure that the file is read and closed again before further processing, otherwise writing to the (still open) file would fail.
Note that the parameter -File is only supported in PowerShell v3 or newer. On older versions you need to replace Get-ChildItem -File with something like this:
Get-ChildItem 'C:\Dir' | Where-Object { -not $_.PSIsContainer } | ...

Unix tail and grep equivalent for Windows

We have the following unix command:
/usr/bin/tail -n 1 %{path} | grep --silent -F "%{message}" && rm -f %{path}%
This:
/usr/bin/tail -n 1 %{path} gets the last line in the file that the path variable refers to
| grep --silent -F "%{message}" pipes the output to another command, grep, which checks if the output of the previous command is equal to the value of message
&& rm -f %{path}% if the values are equal, then delete the file refered to by path
The above line is in a configuration file which is allows for calls to be made to the underlying operating system.
I want to replicate the functionalirty on windows.
I tried this:
command => 'powershell -Command "& {Get-Item $args[0] | ? { (Get-Content $_ -Tail 1).Contains($args[1]) }| Remove-Item -Force}" "'%path%'" "'%message%'"'
This error is thrown:
Error: Expected one of #, {, } at line 15, column 131 (byte 498)
Line 15 is the line in the configuration file which contains the above.
Thanks
PowerShell solution:
$path = 'C:\path\to\your.txt'
$message = 'message'
Get-Item $path | ? { (Get-Content $_ -Tail 1).Contains($message) } | Remove-Item -Force
If you want to run it from a command line, call it like this:
powershell -Command "& {Get-Item $args[0] | ? { (Get-Content $_ -Tail 1).Contains($args[1]) } | Remove-Item -Force}" "'C:\path\to\your.txt'" "'message'"
You can use tailhead.bat (pure batch script utility) that can be used to show lasts/fists lines of a file.Instead of Grep you can use findstr or find :
tailhead.bat tailhead -file=%pathToFile% -begin=-3|find "%message%"

Merge Text Files and Prepend Filename and Line Number - Powershell

I've been trying to adapt the answer to this question: powershell - concatenate N text files and prepend filename to each line
My desired output based on an example of 2 .txt files:
First.txt
lines of
data
Second.txt
more lines
of
data
Output.txt
First1 lines of
First2 data
Second1 more lines
Second2 of
Second3 data
Based on the most similar question I could find the following answer:
Select-String '^' *.txt >output.txt
Would give:
C:\A\Filepath\First.txt:1:lines of
C:\A\Filepath\First.txt:2:data
C:\A\Filepath\Second.txt:1:more lines
C:\A\Filepath\Second.txt:2:of
C:\A\Filepath\Second.txt:3:data
So I was hoping to use -replace to remove the filepath, keep the file name (but remove .txt:), keep the line number (but replace the final : with a space) and keep the text from the line.
Any help would be appreciated reaching the desired output.txt. Thanks
Not beautiful but this is one approach.
Get-ChildItem *.txt |
%{$FILENAME=$_.BaseName;$COUNT=1;get-content $_ |
%{"$FILENAME"+"$COUNT"+" " + "$_";$COUNT++}}|
Out-File Output.txt
The select-string approach is very interesting. The way I would go about it is to use Get-Content. The advantage there is that each line has a readcount property that represents the line number.
Get-ChildItem "C:\temp\*.file" | ForEach-Object{
$fileName = $_.BaseName
Get-content $_ | ForEach-Object{
"{0}{1} {2}" -f $fileName,$_.ReadCount,$_
}
} | Add-Content "C:\temp\output.txt"
Take each file and use Get-Content. With each line we process we send to the output stream a formatted line matching your desired output. No need to count the lines as $_.ReadCount already knows.
Select-String still works
You just need to manipulate the output to match what you want. Using Get-Member we can check the properties of what select-string returns to get our desired output.
Select-String '^' "c:\temp\*.txt" | ForEach-Object{
"{0}{1} {2}" -f $_.Filename,$_.LineNumber,$_.Line
} | Add-Content "C:\temp\output.txt"

Powershell equivalent to sponge in moreutils?

There's a GNU program called sponge that soaks up input before writing to a file so you can do something like this: cat myFile | grep "myFilter" | sponge myFile
Is there a powershell equivalent, so I can work on a file in place, without having to pipe to a temporary file?
Thanks
In Powershell, judicious use of parentheses will force an operation to completely finish before passing data to the next command in the pipeline. The default for piping Get-Content is to pipe line by line to the next command, but with parentheses it must form a complete data set (e.g., load all lines) before continuing:
(Get-Content myFile) | Select-String 'MyFilter' | Set-Content myFile
An alternative that may use less memory (I have not benchmarked it) is to only force the results of Select-String to complete before continuing:
(Get-Content myFile | Select-String 'MyFilter') | Set-Content myFile
You could also assign things to a variable as an additional step. Any technique will load the contents into the Powershell session's memory, so be careful with big files.
Addendum: Select-String returns MatchInfo objects. Using Out-File adds pesky extra blank lines due to the way it tries to format the results as a string, but Set-Content correctly converts each object to its own string as it writes, producing better output. Being that you're coming from *nix and are used to everything returning strings (whereas Powershell returns objects), one way to force string output is to pipe them through a foreach that converts them:
(Get-Content myFile | Select-String 'MyFilter' | foreach { $_.tostring() }) | Set-Content myFile
You can try this :
(Get-content myfile) | where {$_ -match "regular-expression"} | Set-content myfile
or
${full-path-file-name-of-myfile} | where {$_ -match "regular-expression"} | add-content Anotherfile
more easier to keep in mind
two other ways come to mind - they are both the same really just one is a function the other is on the command line. (I don't know sponge on unix so I can't say for certain they mimic it).
here's the first on the command line
Get-Content .\temp.txt |
Select-String "grep" |
foreach-object -begin { [array] $out = #()} -process { $out = $out + ($_.tostring())} -end {write-output $out}
and the second is two create a function to do it
function sponge {
[cmdletbinding()]
Param(
[Parameter(
Mandatory = $True,
ValueFromPipeline = $True)]
[string]$Output
)
Begin {
[array] $out = #()
}
Process {
$out = $out + $Output
}
End {
Write-Output $Out
}
}
Get-Content .\temp2.txt | Select-String "grep" | sponge
HTH,
Matt