Making grep searches sexier ๐ถ๏ธ
grep is a life-saver for many but it is not so good with terminal UX, in this short tutorial I share some tips that can help make your grep experience a bit more pleasant!
Changing grep colors ๐๏ธ
GNU Grep offers the GREP_COLORS
environment variable to modify the grep colors. You can read more about this in the GNU grep manual
The colors are controlled using Select Graphic Rendition (SGR) commands which is a colon-separated list of ANSI color sequences, concatenated via semicolons. Under the hood grep assembles converts this list into a complete SGR sequence (โ\33[โโฆโmโ) which you already might know about. Below is a slightly better combination of colors then plain old red & pink,
export GREP_COLORS='ms=1;38;5;214:fn=1;38;5;154:ln=1;38;5;111'
- [
ms
] Search word = bold orange foreground - [
fn
] Filename = bold green foreground - [
ln
] Line no = bold blue foreground
You can set background colors as well by continuing the color sequence,
fn=1;38;5;154;1;48;5;236
the first part 1;38;5;154
sets the foreground to parrot green color and the part 1:48;5;236
is responsible for setting the background color to darkish gray. This of course works both ways you could set background followed by foreground
Other text styling attributes include,
- Underline:
fn=4;38;5;154
- Bold & Underline:
fn=1;4;38;5;154
- Bold & Italics:
fn=1;3;38;5;154
See the complete chart for other capabilities here
Must include options
grep can be used as a codebase-exploration tool, below are some must include options
- Excluding directories:
The exclude-dir option requires a glob pattern as an argument.
--exclude-dir={_site,.git,node_modules,__pycache__}
- Excluding files
--exclude='*.gitignore'
--w
: only match whole โwordsโ--i
: ignore-case--n
: print line number with output lines--r
: do a recursive search--I
: prevent searching inside binary files
Improving Output Format using AWK
# Utility to print grep searches in a human-friendly way
BEGIN {
FS=":"
file=""
filestarted=0
GREEN_FG="\033[1;38;5;154m"
RESET="\033[m"
}
{
if($1 != file && $1 != ""){
file=$1
print "\n" $1 ":"
filestarted=0;
}
if(filestarted != 0){
print GREEN_FG "|" RESET
}
out=substr($0, length($1 ":" $2 ": "))
print $2 " " out
filestarted=1;
}
The BEGIN
block declares some variables including FS
, which is used to split the input string using :
as a field separator.
Since grepโs output format can be separated into 3 records filename:line_no:match_line
as each line is processed the segregated outputs are available in the following records:
$1
: file-path$2
: line number$3
: the rest of the line
Note that the current line being processed is always available in the record
$0
The FS variable can also be set using the -F
flag,
$ awk -F ":" '/pattern/{action}'
Awk performs any actions associated with the BEGIN
pattern once, before processing the input file/text. Following that we use a very naive logic to print grep results, continue printing the output match line (stored in $3
) until the filename record changes. We are instead getting the substring from $0
as we will have problems printing results that contain a colon (considering our FS is also set to a colon)
The complete and up to date script is available here
All you need to do is just pipe the output of grep to this script, using the -f
option.
grep --winr --color=always | awk -f pretty-grep.awk
Here is how it looks like,
I recommend creating a custom bash function, I have this in my .bash_functions
,
lk (){
grep -wnirI --color=always --exclude='*.gitignore' --exclude-dir={_site,.git,.github} "$@" | awk '{$1=$1};1' | awk -f ~/Documents/.Varshney/scripts/pretty-grep.awk
}
And there you have it, a slightly better grep.