Can you colorize specific lines that are grepped from a file? - bash

I run a weekly CRONTAB that collects hardware info from 40+ remote servers and creates a weekly log file on our report server at the home office. I have a script that I run against this weekly file to output only specific status lines to my display.
#!/bin/sh
# store newest filename to variable
DD_FILE="$(ls -t /home/user/ddinfo/|head -1)"
# List the site name, disk ID (virtual & physical), Status and State of each ID, Failure Prediction for each physical disk, and the site divider
grep -w 'Site\|^ID\|^State\|^Status\|^Failure Predicted\|^##' /home/user/ddinfo/$DD_FILE
echo "/home/user/ddinfo/"$DD_FILE
exit 0
This is a sample output:
Accessing Site: site01
ID : 0
Status : Ok
State : Ready
ID : 0:0:0
Status : Ok
State : Online
Failure Predicted : No
ID : 0:0:1
Status : Ok
State : Online
Failure Predicted : No
################################################
Accessing Site: site02
ID : 0
Status : Ok
State : Ready
ID : 0:0:0
Status : Non-Critical
State : Online
Failure Predicted : Yes
ID : 0:0:1
Status : Ok
State : Online
Failure Predicted : No
################################################
Is there a way to cat / grep / sed / awk / perl / this output so that any lines that end with either Critical or Yes, get colorized?

With GNU grep:
grep --color -E ".*Yes$|.*Critical$|$" file

You could try ack, a very nice alternative to grep:
% ack '(Critical|Yes)$' file
# Or to colorize the whole line:
% ack '(.*(Critical|Yes))$' file
See Beyond grep
Or if you want to see all lines and only colorize specific ones:
use Term::ANSIColor qw/ colored /;
while (<$fh>) {
s/(.*)(Critical|Yes)$/colored(["yellow bold"], $1.$2)/e;
print;
}

To see all lines but have the lines that end in Critical or Yes colorized, try:
awk -v on="$(tput smso)" -v off="$(tput rmso)" '/(Critical|Yes)$/{$0=on $0 off} 1' logfile
This uses tput to create codes suitable for your terminal. For demonstration purposes, I chose the smso/rmso to set and reset the "standout mode." You can use any other feature that tput supports.
Variation
If we want the text in red instead of "standout mode":
awk -v on="$(tput setaf 1)" -v off="$(tput sgr0)" '/(Critical|Yes)$/{$0=on $0 off} 1' logfile
tput setaf 1 is the code to create red. (In tput, red is 1, green is 2, etc.). tput sgr0 is the code to turn off all attributes.
How it works
-v on="$(tput smso)" -v off="$(tput rmso)"
This defines two awk variables, on and off that turn on and turn off whatever color effect we prefer.
/(Critical|Yes)$/{$0=on $0 off}
For any line that ends with Critical or Yes, we add the on code to the front of the line and the off code to the end.
1
This is awk's cryptic shorthand for print-the-line.

You could use Term::ANSIColor module of Perl:
... | perl -pne 'BEGIN { use Term::ANSIColor } /: (Yes|Critical)$/ && { $_ = color("red") . "$_" . color("reset") }'

Thank you for all of your responses. I ended up piping the original grep results to another grep | grep --color=auto '.*\(Yes\|Critical\).*\|$' and got the colorized results I wanted:
grep -i 'site\|^ID\|^State\|^Status\|^Failure Predicted\|^##' /home/user/ddinfo/$DD_FILE | grep --color=auto '.*\(Yes\|Critical\).*\|$'
This the new sample output:

Related

How to send shell script output in a tablular form and send the mail

I am a shell script which will give few lines as a output. Below is the output I am getting from shell script. My script flow is like first it will check weather we are having that file, if I am having it should give me file name and modified date. If I am not having it should give me file name and not found in a tabular form and send email. Also it should add header to the output.
CMC_daily_File.xlsx Not Found
CareOneHMA.xlsx Jun 11
Output
File Name Modified Date
CMC_daily_File.xlsx Not Found
CareOneHMA.xlsx Jun 11
UPDATE
sample of script
#!/bin/bash
if [ -e /saddwsgnas/radsfftor/coffe/COE_daily_File.xlsx ]; then
cd /sasgnas/radstor/coe/
ls -la COE_daily_File.xlsx | awk '{print $9, $6"_"$7}'
else
echo "CMC_COE_daily_File.xlsx Not_Found"
fi
Output
CMC_COE_daily_File.xlsx Jun_11
I thought I might offer you some options with a slightly modified script. I use the stat command to obtain the file modification time in more expansive format, as well as specifying an arbitrary, pre-defined, spacer character to divide the column data. That way, you can focus on displaying the content in its original, untampered form. This would also allow the formatted reporting of filenames which contain spaces without affecting the logic for formatting/aligning columns. The column command is told about that spacer character and it will adjust the width of columns to the widest content in each column. (I only wish that it also allowed you to specify a column divider character to be printed, but that is not part of its features/functions.)
I also added the extra AWK action, on the chance that you might be interested in making the results stand out more.
#!/bin/sh
#QUESTION: https://stackoverflow.com/questions/74571967/how-to-send-shell-script-output-in-a-tablular-form-and-send-the-mail
SPACER="|"
SOURCE_DIR="/saddwsgnas/radsfftor/coe"
SOURCE_DIR="."
{
printf "File Name${SPACER}Modified Date\n"
#for file in COE_daily_File.xlsx
for file in test_55.sh awkReportXmlTagMissingPropertyFieldAssignment.sh test_54.sh
do
if [ -e "${SOURCE_DIR}/${file}" ]; then
cd "${SOURCE_DIR}"
#ls -la "${file}" | awk '{print $9, $6"_"$7}'
echo "${file}${SPACER}"$(stat --format "%y" "${file}" | cut -f1 -d\. | awk '{ print $1, $2 }' )
else
echo "${file}${SPACER}Not Found"
fi
done
} | column -x -t -s "|" |
awk '{
### Refer to:
# https://man7.org/linux/man-pages/man4/console_codes.4.html
# https://www.ecma-international.org/publications-and-standards/standards/ecma-48/
if( NR == 1 ){
printf("\033[93;3m%s\033[0m\n", $0) ;
}else{
print $0 ;
} ;
}'
Without that last awk command, the output session for that script was as follows:
ericthered#OasisMega1:/0__WORK$ ./test_55.sh
File Name Modified Date
test_55.sh 2022-11-27 14:07:15
awkReportXmlTagMissingPropertyFieldAssignment.sh 2022-11-05 21:28:00
test_54.sh 2022-11-27 00:11:34
ericthered#OasisMega1:/0__WORK$
With that last awk command, you get this:

Wrong search result in a file through Bash script

I am searching an event field in a file but is giving wrong output. I am searching gpio-keys event in input devices for which I have written a script, but I'm unable to print anything in output file (in my case I am writing in a button device file it is null always). Please help me to figure out this. Where am I doing wrong in script file?
Bash script:
#!/bin/bash
if grep -q "gpio-keys" /proc/bus/input/devices ; then
EVENT=$(cat /proc/bus/input/devices | grep "Handlers=kbd")
foo= `echo $EVENT | awk '{for(i=1;i<=NF;i++) if($i=="evbug")printf($(i-1))}'`
#foo=${EVENT:(-7)}
echo -n $foo > /home/ubuntu/Setups/buttonDevice
fi
i am still not able to get anything in buttondevce
That's no wonder, since in the input line
H: Handlers=kbd event0
there's nowhere the evbug your awk script is looking for.
I my case it is event0 but it may vary also depends on how kernel allows.
If it is event0 or similar, then it's nonsensical to look for evbug. Change the statement
if($i=="evbug")printf($(i-1))
to
if ($i~"event") print $i
(using regular expression match).
I have rewritten my script like above. but through it, I have got two events(event0, event3) but … my input devices are many but i want the gpio-keys event
Aha - in order to take only the handler line from the gpio-keys section, you can use sed with an address range:
EVENT=`sed -n '/gpio-keys/,/Handlers=kbd/s/.*Handlers=kbd //p' </proc/bus/input/devices`
Prakash, I don't have access to your google drive. But I just want to give you some suggestion:-
foo= `echo $EVENT | awk '{for(i=1;i<=NF;i++) if($i=="evbug")printf($(i-1))}'`
This is old style now. Better use like below:-
foo=$(echo $EVENT | awk '{for(i=1;i<=NF;i++) if($i=="evbug")printf($(i-1))}')
Also always use double quotes "" when echoing a variable. See below:-
echo -n "$foo" > /home/ubuntu/Setups/buttonDevice
Try with the below code it will work for you
#!/bin/bash
if grep "gpio-keys" /proc/bus/input/devices >/dev/null ; then
cat /proc/bus/input/devices | grep "Handlers=kbd" | awk '{for(i=1;i<=NF;i++){ if($i ~ /eve/){printf "%s \n", $i} } }') > /home/ubuntu/Setups/buttonDevice
fi
The output in buttonDevice would be
event0
event1
.
.
.
.
event100

How can I avoid Delimiters replacing Date values while printing in shell script?

For the command:
cat <file_name>.asc | head -1
Output:
10/30/2006,19:41:58,1.4871,1,E
I wanted to append commas to this line to maintain field consistency.
So I tried various combinations like issuing,
Command : echo cat <file_name>.asc | head -1,,,
Output : ,,,30/2006,19:41:58,1.4871,1,E
Command : echo cat <file_name>.asc | head -1,
Output : ,0/30/2006,19:41:58,1.4871,1,E
Command : echo cat <file_name>.asc | head -1",,,"
Output : ,,,30/2006,19:41:58,1.4871,1,E
Command : chk=$(echo cat <file_name>.asc | head -1)
echo ${chk},,,
Output : ,,,30/2006,19:41:58,1.4871,1,E
But my expected output is very simple,
10/30/2006,19:41:58,1.4871,1,E,,,
Actually my logic is to open a file with CAT and do a while operation on each line to check the data and append comma wherever needed and write the output to another file.
If I understood correctly, you want do this conversion:
10/30/2006,19:41:58,1.4871,1,E --> 10/30/2006,19:41:58,1.4871,1,E,,,
If so, then you should use sed util:
cat <file_name>.asc | sed -e 's/$/,,,/' > output_file
echo `head -n 1 cat.asc`,,,
If you need this in a variable, it goes like this:
X=`head -n 1 cat.asc`,,,
This answers the question which you asked. However, you also mention that you want to process a whole file and do some adjustment for each line. In this case, I don't see how this approach will help....

Re-direct output of a shell to a txt file

I have a script written and I want to include a function in the script, that silently logs the console output to a .txt file. The printf used in my shell scripts have colors for certain characters.
A sample:
# Color block
G="\033[32m"
N="\033[0m"
R="\033[31m"
Y="\033[33m"
# MCS Check
mcs=$(cat /home/admin/service-health.txt | grep -i mcs | cut -d ' ' -f 5 | tr . " ")
if [ "$mcs" == "up " ]
then
printf "${Y}MCS${N} Service Status is\t\t |${G}UP${N}\n"
else
printf "${Y}MCS${N} Service Status is\t\t |${R}DOWN${N}\n"
fi
Console output for this will display the color.
This is not mandatory in the .txt logging.
I will then be emailing this .txt to an address using:
sendmail $vdp $eaddr < /home/admin/health-check.txt
I used this block as I want to redirect the output within the script itself:
sudo touch /home/admin/health-check.txt
exec > >(tee -i /home/admin/health-check.txt)
exec 2>&1
But since this is a colored output, I keep getting this in my email:
[33mGSAN[0m Service Status is |[32mUP[0m
[33mMCS[0m Service Status is |[32mUP[0m
[33mTomcat[0m Service Status is |[32mUP[0m
[33mScheduler[0m Service Status is |[32mUP[0m
[33mMaintenance[0m Service Status is |[32mUP[0m
VDP [33mAccess State[0m is |[32mFULL[0m
Any thoughts about stripping colors during redirect? I do not want to use sed to find and replace as this looks tedious.
Thanks.
You can direct the output using the > character. printf "mytext" > out.txt will print "mytext" to the file "out.txt"

Print line after the match in grep [duplicate]

This question already has answers here:
How to show only next line after the matched one?
(14 answers)
Closed 6 years ago.
I'm trying to get the current track running from 'cmus-remote -Q'
Its always underneath of this line
tag genre Various
<some track>
Now, I need to keep it simple because I want to add it to my i3 bar. I used
cmus-remote -Q | grep -A 1 "tag genre"
but that grep's the 'tag' line AND the line underneath.
I want ONLY the line underneath.
With sed:
sed -n '/tag genre/{n;p}'
Output:
$ cmus-remote -Q | sed -n '/tag genre/{n;p}'
<some track>
If you want to use grep as the tool for this, you can achieve it by adding another segment to your pipeline:
cmus-remote -Q | grep -A 1 "tag genre" | grep -v "tag genre"
This will fail in cases where the string you're searching for is on two lines in a row. You'll have to define what behaviour you want in that case if we're going to program something sensible for it.
Another possibility would be to use a tool like awk, which allows for greater compexity in the line selection:
cmus-remote -Q | awk '/tag genre/ { getline; print }'
This searches for the string, then gets the next line, then prints it.
Another possibility would be to do this in bash alone:
while read line; do
[[ $line =~ tag\ genre ]] && read line && echo "$line"
done < <(cmus-remote -Q)
This implements the same functionality as the awk script, only using no external tools at all. It's likely slower than the awk script.
You can use awk instead of grep:
awk 'p{print; p=0} /tag genre/{p=1}' file
<some track>
/tag genre/{p=1} - sets a flag p=1 when it encounters tag genre in a line.
p{print; p=0} when p is non-zero then it prints a line and resets p to 0.
I'd suggest using awk:
awk 'seen && seen--; /tag genre/ { seen = 1 }'
when seen is true, print the line.
when seen is true, decrement the value, so it will no longer true after the desired number of lines are printed
when the pattern matches, set seen to the number of lines to be printed

Resources