How to use tail in combination with sed . - bash

I want to beep a Sound , incase there is any Exception ocured in Log Files .
I am using bash script .
But unfortunately when tail is used in combintaion with sed , it doesn't work .
I have tried with the below commands and posting here .
tail -f mylogs.log | grep "Exception" | sed -e $'s/Exception/Exception\a/'
tail -f mylogs.log | sed -e $'s/Exception/Exception\a/'
tail -f mylogs.log | grep "Exception" | sed -e $'s/Exception/Exception\a/'

The problem is that grep sees that it's not writing to the terminal, so it buffers its output, eventually writing big chunks that sed can process all at once. To tell it to print out lines as soon as they're available, use the --line-buffered option:
tail -f mylogs.log \
| grep --line-buffered Exception \
| sed -u -e $'s/Exception/Exception\a/'
(Note that I've also added the -u flag to sed, which is similar to grep's --line-buffered option. In my testing it didn't seem to make a difference for this command, but I figure it's better to include it just in case.)

Related

Bash Script - tail to file

I have the following in a bash script file watcher.sh.
grep ERROR $ExampleLogFile > $ErrorLogFile
When I run this, it copied the lines from ExampleLogFile to ErrorLogFile that contain ERROR successfully.
I need to make it so it continually monitors the ExampleLogFile for changes and writes those to the ErrorLogFile.
I was thinking of doing the following, but this doesn't work:
tail -f grep ERROR $ExampleLogFile > $ErrorLogFile
It does write some of the lines, but its not the ones containing ERROR.
tail: grep: No such file or directory
tail: ERROR: No such file or directory
Any advise please.
You can use tee command here.
tail -f $ExampleLogFile | grep --line-buffered ERROR | tee $ErrorLogFile
It will store and print to stdout at the same time.
You need:
while :; do grep ERROR $ExampleLogFile > $ErrorLogFile; sleep 2; done
This should achieve what you want without needing the tail command.
If the file will ever be cleared though this will not work as you might expect because the grep will pull only current entries in the $ErrorLogFile.
You can arrange the tail/grep in a pipe
tail -f $ExampleLogFile | grep ERROR > $ErrorLogFile
Remember that this command will never exit by itself (tail will continue to look for additional data). You will have to arrange for some other exit condition (e.g., timeout, explicit kill, etc).
tail -f $ExampleLogFile | grep --line-buffered ERROR > $ErrorLogFile
or paranoic:
stdbuf -oL tail -f $ExampleLogFile | stdbuf -oL grep --line-buffered ERROR > $ErrorLogFile
But most probably you want to include existing lines too. In that case:
tail -n +1 -f $ExampleLogFile | grep --line-buffered ERROR > $ErrorLogFile

How to output bash command to stdout and pipe to another command at the same time?

I'm working on a server and to show detailed GPU information I use these commands:
nvidia-smi
ps -up `nvidia-smi |tail -n +16 | head -n -1 | sed 's/\s\s*/ /g' | cut -d' ' -f3`
However as you can see, nvidia-smi is called twice. How can I make the output of nvidia-smi go to output and pipe to another command at the same time?
Use tee:
ps -up `nvidia-smi |tee /dev/stderr |tail -n +16 | head -n -1 | sed 's/\s\s*/ /g' | cut -d' ' -f3`
Since stdout is piped, you can't make a copy to it, so I picked stderr to show output.
If /dev/stderr is not available, use /proc/self/fd/2.

Pass the argument to if condition used in pipe

I am trying to write a script which extracts data from the file "nohup.out" using tail -f and executing dig command on condition.
#!/bin/bash
nohup proxychains firefox
tail -f nohup.out | xargs if [[ {} == *"denied"* ]]
then
dig -x `cut -d '-' -f 6 {} | cut -d ':' -f 1`&;
fi
Output of nohup.out is
|S-chain|-<>-10.1.1.16:80-<><>-93.184.220.29:80-<--denied
|S-chain|-<>-10.1.1.16:80-<><>-93.184.220.29:80-<--denied
|S-chain|-<>-10.1.1.16:80-<><>-216.58.209.77:443-<><>-OK
|S-chain|-<>-10.1.1.16:80-<><>-46.28.247.89:443-<><>-OK
With the below command I am able to extract the IP for reverse DNS lookup.
cut -d '-' -f 6 | cut -d ':' -f 1
I am not able to find a way to pass the argument to cut and if command.
What you need is to convert the if statement into an argument to bash. Doing a simplistic transform, assuming that the code in the question has a chance of working, you get:
tail -f nohup.out |
xargs -I '{}' bash -c "if [[ {} == *"denied"* ]]; then dig -x $(cut -d '-' -f 6 {} | cut -d ':' -f 1) & fi"
This is exactly the same basic treatment as was needed for a for loop being executed by nohup — you need a shell to run the built-in command. See Why can't I use Unix nohup with Bash for loop? for an exactly analogous situation.
However, on further reflection, you want to cut the string which is the IP address, not the file with that as a name, so the command needs to echo the string into the cut commands. You also have to tear your hair getting the sub-commands executed correctly; you need a backslash before the $ of $(…), or before each of the back-ticks if you insist on using `…` notation, as well as using backslash-double-quote to protect the angle-brackets in the string.
tail -f nohup.out |
xargs -I '{}' bash -c "if [[ '{}' != *denied* ]]; then echo dig -x \"\$(echo '{}' | cut -d '-' -f 6 | cut -d ':' -f 1)\" & fi"
Now we need to debate the use of the condition and two cut commands (and the general hair loss). You could use:
tail -f nohup.out |
grep -v denied |
xargs -I '{}' bash -c "echo dig -x \$(echo '{}' | cut -d '-' -f 6 | cut -d ':' -f 1) &"
or, more sensibly:
tail -f nohup.out |
awk -F '[-:]' '/denied/ { next } { print "dig -x " $7 " &" }' |
sh -x
or any of a myriad other ways to do it.
awk -F- '!/denied/ {print $6}'
splits each input line in fields separated by -, ignores the lines matching denied and extracts the 6th field of the remaining lines. With you example it outputs:
216.58.209.77:443
46.28.247.89:443

xargs: exec command with prompt

I'm trying to do the following with xargs
pacman -Q | grep xf86-video | awk '{print $1}' | xargs pacman -R to remove all xf86-video-* driver on my machine. To make the question more clear, here is the output of pacman -Q | grep xf86-video | awk '{print $1}':
xf86-video-ark
xf86-video-ati
xf86-video-dummy
xf86-video-fbdev
xf86-video-glint
xf86-video-i128
xf86-video-intel
xf86-video-mach64
xf86-video-neomagic
xf86-video-nouveau
....
when I redirect the result to xargs, the output looks like this:
The point is, the command which xargs is about to execute need user to do some additional input(as you can see it needs a Yes/No), but xargs automatically add a unknown symbol #, and exit, which causes my purpose UNACHIEVED.
WHY xargs would do this or, what can I do to use xargs for command with prompt?
You can use
xargs -a <(pacman -Q | awk '/xf86-video/{print $1}') pacman -R
Explanation:
Without further arguments xargs does not work with interactive (command line) applications.
The reason for that is, that by defaultxargs gets its input from stdin but interactive applications also expect input from stdin. To prevent the applications from grabbing input that is intended for xargs, xargs redirects stdin from /dev/null for the applications it runs. This leads to the application just receiving an EOF. (Running just pacman -R SOMEPACKAGE and pressing Ctrl+D has the same effect).
To get xargs to work with interactive commands you have to use the --arg-file=FILE argument (short -a FILE). This tells xargs to get the arguments from FILE. This also leaves stdin unchanged.
So you could either put your package list into a temporary file
pacman -Q | awk '/xf86-video/{print $1}' > /tmp/packagelist
xargs -a /tmp/packagelist pacman -R
rm /tmp/packagelist
or you can use zsh's process substitution mechanism <(list). When executing a line with <(list), <(list) is replaced by a filename from where the output of list can be read.
xargs -a <(pacman -Q | awk '/xf86-video/{print $1}') pacman -R
The single # you get is not from xargs but from zsh itself. If the shell options PROMPT_CR and PROMPT_SP are set (which both are by default) zsh tries to preserve partial lines, that is lines that did not end with a newline. To signify that such a line has been preserved zsh prints an inverse+bold character at the end of that line, by default % for normal users and # for root.
You can eliminate the use of xargs with a single hyphen
Additionally, if stdin is not from a terminal and a single hyphen (-) is passed as an argument, targets will be read from stdin.
— https://archlinux.org/pacman/pacman.8.html
pacman -Q | awk '/xf86-video/{print $1}' | pacman -R -
You can use xargs, though ...
If you wish to use xargs, use it's -o, --open-tty option:
--open-tty
-o
Reopen stdin as /dev/tty in the child process before executing the command, thus allowing that command to be associated to the terminal while xargs reads from a different stream, e.g. from a pipe. This is useful if you want xargs to run an interactive application.
grep -lz PATTERN * | xargs -0o vi ①
— https://www.gnu.org/software/findutils/manual/html_node/find_html/xargs-options.html
① That should be an uppercase Z grep option (bug filed).
For your particular case:
pacman -Q | awk '/xf86-video/{print $1}' | xargs -o pacman -R
You need to run pacman the second time with the --noconfirm option:
pacman -Q | grep xf86-video | awk '{print $1}' | xargs pacman -R --noconfirm
This will disable 'are you sure' messages, and do things without requiring input.

grep whole file and tail it without using "line-buffered" option in grep

I have BusyBox v1.21.0 installed and it has got very basic grep operation, no --line-buffered option
grep sync_complete /var/log/messages
tail -f /var/log/messages | grep sync_complete
is it possible to combine above commands into single line command? thanks!
Use -n +1 to make tail read the file from the start:
tail -n +1 -f /var/log/messages | grep sync_complete

Resources