Is that possible to use grep
on a continuous stream?
What I mean is sort of a tail -f <file>
command, but with grep
on the output in order to keep only the lines that interest me.
I've tried tail -f <file> | grep pattern
but it seems that grep
can only be executed once tail
finishes, that is to say never.
tail -f file
works (I see the new output in real time)
Turn on grep
's line buffering mode when using BSD grep (FreeBSD, Mac OS X etc.)
tail -f file | grep --line-buffered my_pattern
It looks like a while ago --line-buffered
didn't matter for GNU grep (used on pretty much any Linux) as it flushed by default (YMMV for other Unix-likes such as SmartOS, AIX or QNX). However, as of November 2020, --line-buffered
is needed (at least with GNU grep 3.5 in openSUSE, but it seems generally needed based on comments below).
I use the tail -f <file> | grep <pattern>
all the time.
It will wait till grep flushes, not till it finishes (I'm using Ubuntu).
I think that your problem is that grep uses some output buffering. Try
tail -f file | stdbuf -o0 grep my_pattern
it will set output buffering mode of grep to unbuffered.
grep
.
unbuffer
(in the expect-dev
package on debian) is king. So I'd use unbuffer over stdbuf.
top
with stdbuf and unbuffer). And there is really no 'magic' solution: unbuffer fails sometimes too, for example awk uses different buffer implementation (stdbuf will fail too).
stdbuf
, `unbuffer, and stdio buffering at pixelbeat.org/programming/stdio_buffering
If you want to find matches in the entire file (not just the tail), and you want it to sit and wait for any new matches, this works nicely:
tail -c +0 -f <file> | grep --line-buffered <pattern>
The -c +0
flag says that the output should start 0
bytes (-c
) from the beginning (+
) of the file.
In most cases, you can tail -f /var/log/some.log |grep foo
and it will work just fine.
If you need to use multiple greps on a running log file and you find that you get no output, you may need to stick the --line-buffered
switch into your middle grep(s), like so:
tail -f /var/log/some.log | grep --line-buffered foo | grep bar
you may consider this answer as enhancement .. usually I am using
tail -F <fileName> | grep --line-buffered <pattern> -A 3 -B 5
-F is better in case of file rotate (-f will not work properly if file rotated)
-A and -B is useful to get lines just before and after the pattern occurrence .. these blocks will appeared between dashed line separators
But For me I prefer doing the following
tail -F <file> | less
this is very useful if you want to search inside streamed logs. I mean go back and forward and look deeply
grep -C 3 <pattern>
, replaces -A <N> and -B <N> if N is same.
Didn't see anyone offer my usual go-to for this:
less +F <file>
ctrl + c
/<search term>
<enter>
shift + f
I prefer this, because you can use ctrl + c
to stop and navigate through the file whenever, and then just hit shift + f
to return to the live, streaming search.
sed would be a better choice (stream editor)
tail -n0 -f <file> | sed -n '/search string/p'
and then if you wanted the tail command to exit once you found a particular string:
tail --pid=$(($BASHPID+1)) -n0 -f <file> | sed -n '/search string/{p; q}'
Obviously a bashism: $BASHPID will be the process id of the tail command. The sed command is next after tail in the pipe, so the sed process id will be $BASHPID+1.
$BASHPID+1
) will be yours is false in many situations, and this does nothing to solve the buffering problem which is probably what the OP was trying to ask about. In particular, recommending sed
over grep
here seems like merely a matter of (dubious) preference. (You can get p;q
behavior with grep -m 1
if that's the point you are attempting to deliver.)
--line-buffered
did not. I sincerely do not understand the minus 1.
Yes, this will actually work just fine. Grep
and most Unix commands operate on streams one line at a time. Each line that comes out of tail will be analyzed and passed on if it matches.
grep
is the last command in the pipe chain, it will act as you explain. However, if it's in the middle it will buffer around 8k output at a time.
Coming some late on this question, considering this kind of work as an important part of monitoring job, here is my (not so short) answer...
Following logs using bash
1. Command tail
This command is a little more porewfull than read on already published answer
Difference between follow option tail -f and tail -F, from manpage: -f, --follow[={name|descriptor}] output appended data as the file grows; ... -F same as --follow=name --retry ... --retry keep trying to open a file if it is inaccessible This mean: by using -F instead of -f, tail will re-open file(s) when removed (on log rotation, for sample). This is usefull for watching logfile over many days. Ability of following more than one file simultaneously I've already used: tail -F /var/www/clients/client*/web*/log/{error,access}.log /var/log/{mail,auth}.log \ /var/log/apache2/{,ssl_,other_vhosts_}access.log \ /var/log/pure-ftpd/transfer.log For following events through hundreds of files... (consider rest of this answer to understand how to make it readable... ;) Using switches -n (Don't use -c for line buffering!). By default tail will show 10 last lines. This can be tunned: tail -n 0 -F file Will follow file, but only new lines will be printed tail -n +0 -F file Will print whole file before following his progression.
2. Buffer issues when piping:
If you plan to filter ouptuts, consider buffering! See -u
option for sed
, --line-buffered
for grep
, or stdbuf
command:
tail -F /some/files | sed -une '/Regular Expression/p'
Is (a lot more efficient than using grep
) a lot more reactive than if you does'nt use -u
switch in sed
command.
tail -F /some/files |
sed -une '/Regular Expression/p' |
stdbuf -i0 -o0 tee /some/resultfile
3. Recent journaling system
On recent system, instead of tail -f /var/log/syslog
you have to run journalctl -xf
, in near same way...
journalctl -axf | sed -une '/Regular Expression/p'
But read man page
, this tool was built for log analyses!
4. Integrating this in a bash script
Colored output of two files (or more) Here is a sample of script watching for many files, coloring ouptut differently for 1st file than others: #!/bin/bash tail -F "$@" | sed -une " /^==> /{h;}; //!{ G; s/^\\(.*\\)\\n==>.*${1//\//\\\/}.*<==/\\o33[47m\\1\\o33[0m/; s/^\\(.*\\)\\n==> .* <==/\\o33[47;31m\\1\\o33[0m/; p;}" They work fine on my host, running: sudo ./myColoredTail /var/log/{kern.,sys}log Interactive script You may be watching logs for reacting on events? Here is a little script playing some sound when some USB device appear or disappear, but same script could send mail, or any other interaction, like powering on coffe machine... #!/bin/bash exec {tailF}< <(tail -F /var/log/kern.log) tailPid=$! while :;do read -rsn 1 -t .3 keyboard [ "${keyboard,}" = "q" ] && break if read -ru $tailF -t 0 _ ;then read -ru $tailF line case $line in *New\ USB\ device\ found* ) play /some/sound.ogg ;; *USB\ disconnect* ) play /some/othersound.ogg ;; esac printf "\r%s\e[K" "$line" fi done echo exec {tailF}<&- kill $tailPid You could quit by pressing Q key.
This one command workes for me (Suse):
mail-srv:/var/log # tail -f /var/log/mail.info |grep --line-buffered LOGIN >> logins_to_mail
collecting logins to mail service
you certainly won't succeed with
tail -f /var/log/foo.log |grep --line-buffered string2search
when you use "colortail" as an alias for tail, eg. in bash
alias tail='colortail -n 30'
you can check by type alias if this outputs something like tail isan alias of colortail -n 30
. then you have your culprit :)
Solution:
remove the alias with
unalias tail
ensure that you're using the 'real' tail binary by this command
type tail
which should output something like:
tail is /usr/bin/tail
and then you can run your command
tail -f foo.log |grep --line-buffered something
Good luck.
Use awk(another great bash utility) instead of grep where you dont have the line buffered option! It will continuously stream your data from tail.
this is how you use grep
tail -f <file> | grep pattern
This is how you would use awk
tail -f <file> | awk '/pattern/{print $0}'
{print $0}
is redundant, as printing is the default action when a condition passes.)
Success story sharing
strace
. Without the--line-buffered
, it won't work.tail -f | grep
, and--line-buffered
solves it for me (on Ubuntu 14.04, GNU grep version 2.16). Where is the "use line buffering if stdout is a tty" logic implemented? In git.savannah.gnu.org/cgit/grep.git/tree/src/grep.c,line_buffered
is set only by the argument parser.--line-buffered
I get no output. However, after testing, it looks like GNU grep does what you describe. So like most things Unix, it depends on your platform's implementation. Since the question did not specify platform, your information appears to be false - after reviewing the code for BSD grep and comparing it to GNU grep, the behavior is definitely controlled by the --line-buffered option. It's just that only GNU grep flushes by default.