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%"
Related
The original code was this:
curl -sL https://ftp.apnic.net/stats/apnic/delegated-apnic-latest | \
grep "apnic|JP|ipv4" | \
awk -F '|' '{ printf("%s/%d\n", $4, 32-log($5)/log(2)) }' | \
tee JP_IPv4.txt
I wanted to convert it to PowerShell, this is how far I got:
Invoke-WebRequest -Uri "https://ftp.apnic.net/stats/apnic/delegated-apnic-latest" |
Select-String -Pattern "afrinic\|ZA\|ipv6" -ca | Select-Object -exp line |
I need to figure out how to convert the last part that uses awk command to PowerShell.
I use PowerShell 7.3
The awk script appears to calculate the subnet prefix length from the network size.
You can replace the awk log(...) function call with [Math]::Log(...) in PowerShell to perform the exact same calculation:
# this takes care of the `curl` part
Invoke-WebRequest -Uri "https://ftp.apnic.net/stats/apnic/delegated-apnic-latest" -OutFile delegated_networks.txt
# this takes care of the `grep` part
Get-Content delegated_networks.txt |Where-Object {$_ -like 'apnic|JP|ipv4|*'}
# this emulates the `awk | tee` part
$data|ForEach-Object {
# -F '|'
$cells = $_.Split('|')
# '{ printf("%s/%d\n", $4, 32-log($5)/log(2)) }'
'{0}/{1}' -f $cells[3],(32 - [Math]::Log($cells[4])/[Math]::Log(2))
} |Tee-Object -FilePath JP_ipv4.txt
As #mklement0 kindly points out, you can also combine the log-conversion operation into a single call in PowerShell:
PS ~>[Math]::Log(4096)/[Math]::Log(2)
12
PS ~>[Math]::Log(4096, 2)
12
I want to iterate over folders and execute a command per folder, ignoring the already processed ones.
For this I'm trying to concatenate variables to produce a command and then execute this string.
But PowerShell (v7.2.5) is removing the variables from concatenation (or replacing them with empty strings).
I tried many different syntaxes like $($var1) + $($var2) or "$var1 $var2", none of them work. My current approach (it will run with Invoke-Expression -Command instead of echo):
$arguments = "-a -b -c"
$exe = "C:\foo\bar.exe"
$targetdir = "C:\path\"
Get-ChildItem $targetdir -Directory | ForEach-Object -Parallel {If($_.FullName.Contains("processed")){continue}; echo ("{0} -d {1} {2}" -f $($exe),$($_.FullName),$($arguments));} -ThrottleLimit 8
Expected:
C:\foo\bar.exe -d C:\path\101 -a -b -c
C:\foo\bar.exe -d C:\path\102 -a -b -c
C:\foo\bar.exe -d C:\path\103 -a -b -c
Output:
-d C:\path\101
-d C:\path\102
-d C:\path\103
Why is PowerShell removing paths or arguments from the concatenation and how do I fix this?
As stated in the the ForEach-Object MS Docs:
The ForEach-Object -Parallel parameter set runs script blocks in parallel on separate process threads. The $using: keyword allows passing variable references from the cmdlet invocation thread to each running script block thread.
Do not use continue outside of a loop, switch, or trap:
Using continue inside a pipeline, such as a ForEach-Object script block, not only exits the pipeline, it potentially terminates the entire runspace.
You can use return to emulate the behavior that continue has on a loop:
$arguments = '-a -b -c'
$exe = 'C:\foo\bar.exe'
$targetdir = 'C:\path'
Get-ChildItem $targetdir -Directory | ForEach-Object -Parallel {
if($_.FullName.Contains('processed')) {
return
}
"{0} -d {1} {2}" -f $using:exe, $_.FullName, $using:arguments
} -ThrottleLimit 8
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"
}
I have a tool that logs some data onto a file. I'd like to tail the file and send the last line of data via mosquitto_pub.
I've used powershell "Get-Content" command without succes.
Here's my command:
Get-Content -Path "C:\test.txt" -Wait | .\mosquitto_pub.exe -t "Events"
But nothing is published by mosquitto_pub.
If I use Get-Content -Path "C:\test.txt" -Wait
I see the tail of the file in stdout.
What's wrong with my solution?
Thanks!
Read this Q and A.
An alternate approach
$minsToRunFor = 10
$secondsToRunFor = $minsToRunFor * 60
foreach ($second in $secondsToRunFor){
$lastline = Get-Content -Path "C:\test.txt" | Select-Object -last 1
# added condition as per VonPryz's good point
# (otherwise will add lastline regardless of whether it's new or not)
if ($lastline -ne $oldlastline){
.\mosquitto_pub.exe -t "Events" -m "$lastline"
}
$oldlastline = $lastline
Start-Sleep 100
}
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
}
}