how to omit grep output in conditional statement - macos

On a Mac, I want to determine if there are any sleep assertions present, using pmset. If there are, extract only that information and omit unnecessary information.
If grep returns nothing I want to print "Nothing".
if pmset -g | grep pre ; then pmset -g | grep pre | cut -d'(' -f2 | cut -d')' -f1 ; else printf "Nothing\n" ; fi
The problem is that the first grep result is printed, and so is the formatted one. For example this is what I get if a backup is in progress:
sleep 15 (sleep prevented by backupd)
sleep prevented by backupd
I don't want the first line, and want to discard it. I only want the second line to print ("sleep prevented by backupd").
If the grep result is empty I want to indicate that with the text "Nothing". The above script works OK for that.
There are probably many more elegant solutions but I've been searching days for one.

If i understand your question properly, you simply need to discard the output of first grep irrespective of the output it provides. If it's so, then you can use -q option provided by grep.
From the man page for 'grep':
-q, --quiet, --silent
Quiet; do not write anything to standard output. Exit immediately with zero status if any match is found, even if an error was
detected. Also see the -s or --no-messages option. (-q is specified by POSIX.)
Something like this:
if ifconfig | grep -q X; then
ifconfig | grep Mi | cut -d'(' -f2
else
printf "Nothing\n"
fi
Obviously in the above example, output of ifconfig will not change every time. Just used as an example. ;)

Redirect the output to /dev/null:
if pmset -g | grep pre >/dev/null 2>&1 ; then
pmset -g | grep pre | cut -d'(' -f2 | cut -d')' -f1
else
printf "Nothing\n"
fi

This is maybe a little more succinct. It gets grep to only output the part of the line that matches the pattern instead of the whole line, by using grep -o:
#!/bin/bash
SLEEP=$(pmset -g | grep -o "sleep prevented.*[^)]")
if [ -z "$SLEEP" ]; then
echo Nothing
else
echo $SLEEP
fi
The pattern is sleep prevented and any characters following until a ) is encountered.

Related

bash: pipe continuously into a grep

Not sure how to explain this but, what I am trying to achieve is this:
- tailing a file and grepping for a patter A
- then I want to pipe into another customGrepFunction where it matches pattern B, and if B matches echo something out. Need the customGrepFunction in order to do some other custom stuff.
The sticky part here is how to make the grepCustomFunction work here.In other words when only patternA matches echo the whole line and when both patterA & patternB match printout something custom:
when I only run:
tail -f file.log | grep patternA
I can see the pattenA rows are being printed/tailed however when I add the customGrepFunction nothing happens.
tail -f file.log | grep patternA | customGrepFunction
And the customGrepFunction should be available globally in my bin folder:
customGrepFunction(){
if grep patternB
then
echo "True"
fi
}
I have this setup however it doesn't do what I need it to do, it only echos True whenever I do Ctrl+C and exit the tailing.
What am I missing here?
Thanks
What's Going Wrong
The code: if grep patternB; then echo "true"; fi
...waits for grep patternB to exit, which will happen only when the input from tail -f file.log | grep patternA hits EOF. Since tail -f waits for new content forever, there will never be an EOF, so your if statement will never complete.
How To Fix It
Don't use grep on the inside of your function. Instead, process content line-by-line and use bash's native regex support:
customGrepFunction() {
while IFS= read -r line; do
if [[ $line =~ patternB ]]; then
echo "True"
fi
done
}
Next, make sure that grep isn't buffering content (if it were, then it would be written to your code only in big chunks, delaying until such a chunk is available). The means to do this varies by implementation, but with GNU grep, it would look like:
tail -f file.log | grep --line-buffered patternA | customGrepFunction

nslookup capture stderr in a variable and display

In a shell script I am running nslookup on number of URLs
Sometimes some url returns cannot resolv error. I need to capture those errors in a variable.
here is code for nslookup which gets ip address returned
output=$(nslookup "$URL" | grep Add | grep -v '#' | cut -f 3 -d ' ' | awk 'NR>1' )
Now in same variable output, I want to capture the error
nslookup: can't resolve
Stdout I am capturing in a file.
I have tried different version of re-directions - 2>&1 and others but error does not get assigned to variable. I do not want the error to be re-directed to separate file but want it to be recorded in above output variable.
As long as you are using awk, you can simplify things considerably
nslookup "$URL" 2>&1 |
awk -e '/Add/ && !/#/ && NR > 1 {print $2}'
-e '/resolve|NXDOMAIN/ { print "error" }'
Where one line has been broken into three for clarity. I cannot reproduce the problem you say you have 2&>1 nor do I believe it should fail.
The redirection of stderr works when you use
output=$(nslookup "$URL" 2>&1 | grep Add | grep -v '#' | cut -f 3 -d ' ' | awk 'NR>1')
but it is futile since you filter it out immediately with the grep Add. You need to rethink your logic and what you really want. Maybe a better approach is
output=$(nslookup "$URL" 2>&1)
case $output in
(nslookup:*) ;;
(*) output=$(echo "$output" | grep Add | ...);;
esac

'tail -f' doesn't give single lines when piped through grep'

I am launching a website, and I wanted to setup a Bash one-liner so when someone hits the site it would make a beep using the internal buzzer.
So far it's working using the following.
tail -f access_log | while read x ; do echo -ne '\007' $x '\n' ; done
Tail follows the access_log and dumps to STDOUT, get STDOUT line at a time, echo the line with '\007' "internal beep hex code", and done...
This works like a beauty... Every hit shows the line from the log and beeps... However, it got annoying very quickly, so ideally I wanted to filter the tail -f /access/log before it's piped into the while so that read only gets lines I care about. I was thinking grep "/index.php" would be a good indication of visitors...
This is where the issue is...
I can do...
tail -f access_log | while read x ; do echo -ne '\007' $x '\n' ; done
beeps on everything
and i can do...
tail -f access_log | grep "/index.php"
and pages are shown with no beep, but when i do
tail -f access_log | grep "/index.php" | while read x ; do echo -ne '\007' $x '\n' ; done
Nothing happens, no line from log, no beep.
I think the grep is messing it up somewhere, but I can't figure out where.
I'd love it to be a one liner, and I know it should really be done in a script and would be easier, but it doesn't explain why the above, which I think should work, isn't.
Grep's output is buffered when it's used in a pipe. Use --line-buffered to force it to use line buffering so it outputs lines immediately.
tail -f access_log | grep --line-buffered "/index.php" | while read x ; do echo -ne '\007' $x '\n' ; done
You could also combine the grep and while loop into a single awk call:
tail -f access_log | awk '/\/index.php/ { print "\007" $0 }'
Grep buffers output when standard output is not a terminal. You need to pass the --line-buffered switch to grep to force it to flush standard output whenever it writes a line.
Using sed -u for unbuffered:
Lighter than awk and grep, using sed could be simple, quick and efficient:
tail -f access.log | sed -une "s#/index.php#&\o7#p"
sed will replace /index.php by found string & plus beep: \o7, then print lines where something was replaced. With -u, sed will read lines by lines, unbuffered.
path='/index.php'
tail -f access.log | sed -une "s#${path}#&\o7#p"

what does grep -v '^#' do

My program looks like this.
ALL=`cat $1 | grep -v '^#' | wc -l`
FINISHED="0"
for i in `cat $1 | grep -v '^#'`; do
echo "PROBE $i"
I will be doing some operation
FINISHED=`echo $FINISHED"+1"|bc`
I will run this script by giving a file name as parameter where a list of probes will be present.
I have 2 questions
What does grep -v '^#' mean. I learnt that '^ is usually used to matching a particular string. But in the file name which I give there is no #. Moreover I am getting the total number of probes for cat $1 | grep -v '^#' | wc -l.
echo $FINISHED"+1"|bc. Here any idea as to why the developer as added |bc?
^ means "start of line"
# is the literal character #
-v means "invert the match" in grep, in other words, return all non matching lines.
Put those together, and your expression is "select all lines that do not begin with #"
| is the pipe character, it takes the output of the command on the left hand side, and uses it as the input of the command on the right hand side. bc is like a command line calculator (to do basic math).
I would use this to exclude comments from the code I'm reading. So all comment lines start with # and I don't want to see them if there are too many of them.
grep -v '^#'
We have different ways for calculation. Pick the one which you like.
a=`echo 1+1 | bc`; echo $a
b=$((1+1)); echo $b
c=`expr 1 + 1`; echo $c
let d=1+1; echo $d

Bash grep variable from multiple variables on a single line

I am using GNU bash, version 4.2.20(1)-release (x86_64-pc-linux-gnu). I have a music file list I dumped into a variable: $pltemp.
Example:
/Music/New/2010s/2011;Ziggy Marley;Reggae In My Head
I wish to grep the 3rd field above, in the Master-Music-List.txt, then continue another grep for the 2nd field. If both matched, print else echo "Not Matched".
So the above will search for the Song Title (Reggae In My Head), then will make sure it has the artist "Shaggy" on the same line, for a success.
So far, success for a non-variable grep;
$ grep -i -w -E 'shaggy.*angel' Master-Music-MM-Playlist.m3u
$ if ! grep Shaggy Master-Music-MM-Playlist.m3u ; then echo "Not Found"; fi
$ grep -i -w Angel Master-Music-MM-Playlist.m3u | grep -i -w shaggy
I'm not sure how to best construct the 'entire' list to process.
I want to do this on a single line.
I used this to dump the list into the variable $pltemp...
Original: \Music\New\2010s\2011\Ziggy Marley - Reggae In My Head.mp3
$ pltemp="$(cat Reggae.m3u | sed -e 's/\(.*\)\\/\1;/' -e 's/\(.*\)\ -\ /\1;/' -e 's/\\/\//g' -e 's/\\/\//g' -e 's/.mp3//')"
If you realy want to "grep this, then grep that", you need something more complex than grep by itself. How about awk?
awk -F';' '$3~/title/ && $2~/artist/ {print;n=1;exit;} END {if(n=0)print "Not matched";}'
If you want to make this search accessible as a script, the same thing simply changes form. For example:
#!/bin/sh
awk -F';' -vartist="$1" -vtitle="$2" '$3~title && $2~artist {print;n=1;exit;} END {if(n=0)print "Not matched";}'
Write this to a file, make it executable, and pipe stuff to it, with the artist substring/regex you're looking for as the first command line option, and the title substring/regex as the second.
On the other hand, what you're looking for might just be a slightly more complex regular expression. Let's wrap it in bash for you:
if ! echo "$pltemp" | egrep '^[^;]+;[^;]*artist[^;]*;.*title'; then
echo "Not matched"
fi
You can compress this to a single line if you like. Or make it a stand-along shell script, or make it a function in your .bashrc file.
awk -F ';' -v title="$title" -v artist="$artist" '$3 ~ title && $2 ~ artist'
Well, none of the above worked, so I came up with this...
for i in *.m3u; do
cat "$i" | sed 's/.*\\//' | while read z; do
grep --color=never -i -w -m 1 "$z" Master-Music-Playlist.m3u \
| echo "#NotFound;"$z" "
done > "$i"-MM-Final.txt;
done
Each line is read (\Music\Lady Gaga - Paparazzi.mp3), the path is stripped, the song is searched in the Master Music List, if not found, it echos "Not Found", saved into a new playlist.
Works {Solved}
Thanks anyway.

Resources