#!/bin/sh - # # This is a filter that prints parts of lines (or whole # lines) that match a regular expression in bold. # Non-matching text is printed unmodified. # Examples: # # mount | bold soft-updates # Show "mount" ouput with all occurences of the word # "soft-updates" highlighted. # # tail -f /var/log/messages | bold -l kernel # Print syslog output (and wait for more to come, # see the tail(1) manpage) and highlight all lines # that conmtain the word "kernel". # # Tested on BSD and Solaris. # (Solaris requires using ksh or /usr/xpg4/bin/sh.) # # Copyright (C) 1998-2008 Oliver Fromme # Use and distribute under BSD license. # IGN_CASE=0 WHOLE_LINES=0 RUN_TESTS=0 VIDEO_CMODE="" Usage() { ME="${0##*/}" cat <<-tac >&2 Usage: $ME [-burBil] [ ...] or: $ME -t Options for video attributes (default depends on executable name): -b bold -u underline -r reverse -B blink Other options: -i Ignore case (i.e. comparison is case-insensitive). -l Apply attributes to whole lines, not just the matching part. -t Run a built-in attribute test on your terminal. Notes: Attributes can be combined (e.g. -bu = bold + underline). Not all attributes are supported on all terminals. is an extended regular expression. tac } CM_NORM='\033[m' CM_BOLD='\033[1m' CM_UNDL='\033[4m' CM_BLNK='\033[5m' CM_REVS='\033[7m' while :; do case "$1" in ""|[!-]*) break ;; --) shift break ;; -[!ltbuBir]*) Usage exit 1 ;; esac case "$1" in -*i*) IGN_CASE=1 ;; esac case "$1" in -*l*) WHOLE_LINES=1 ;; esac case "$1" in -*t*) RUN_TESTS=1 ;; esac case "$1" in -*b*) VIDEO_CMODE="${VIDEO_CMODE}$CM_BOLD" ;; esac case "$1" in -*u*) VIDEO_CMODE="${VIDEO_CMODE}$CM_UNDL" ;; esac case "$1" in -*B*) VIDEO_CMODE="${VIDEO_CMODE}$CM_BLNK" ;; esac case "$1" in -*r*) VIDEO_CMODE="${VIDEO_CMODE}$CM_REVS" ;; esac shift done case "$RUN_TESTS" in 1) INDENT="" SPACES="`printf '%30s' ''`" for BO in "" b+bold; do for UL in "" u+underline; do for IV in "" r+reverse; do for BL in "" B+blink; do OPTS="-${BO%+*}${UL%+*}${IV%+*}${BL%+*}" TEXT="${BO#*+} ${UL#*+} ${IV#*+} ${BL#*+} " REXP="[^ ].*" case "$OPTS" in "-") OPTS="" TEXT="none" REXP="xxx" ;; esac echo "$INDENT" $TEXT | $0 $OPTS "$REXP" case "$INDENT" in "") INDENT="$SPACES" ;; *) INDENT="" ;; esac done done done done exit ;; esac if [ -z "$VIDEO_CMODE" ]; then case "$0" in *link) VIDEO_CMODE="$CM_BLNK" ;; *line) VIDEO_CMODE="$CM_UNDL" ;; *erse) VIDEO_CMODE="$CM_REVS" ;; *) VIDEO_CMODE="$CM_BOLD" ;; esac fi if [ $# -lt 1 ]; then Usage exit 1 fi RE="$1" if [ -z "$1" ]; then RE=".*" fi shift # # The following requires some explanations. # # Why awk instead of sed? Because sed always buffers the # last line, so this wont work with "tail -f" (the last # line would always be missing until more output occurs; # see the second example above). On the other hand, awk # always processes each line as it comes, so this works # fine with "tail -f". # # Why "case/esac" instead of "if [ ]; then"? Because # "[" (which is a link to "test") is sometimes an external # program, but "case" is _always_ a shell-builtin. So # the latter is generally more efficient. # # The quoting around "$RE" might look confusing and # strange at first, but it's correct and necessary to # protect the variable, which could contain anything # (including whitespace). # case "${WHOLE_LINES}${IGN_CASE}" in 10) nawk '{ if ($0 ~ "'"$RE"'") print "'"$VIDEO_CMODE"'" $0 "'"$CM_NORM"'" else print }' "$@" ;; 11) nawk 'BEGIN{ re = tolower("'"$RE"'") } { if (tolower($0) ~ re) print "'"$VIDEO_CMODE"'" $0 "'"$CM_NORM"'" else print }' "$@" ;; 00) nawk '{ gsub("'"$RE"'", "'"$VIDEO_CMODE"'&'"$CM_NORM"'") print }' "$@" ;; 01) nawk 'BEGIN{ re = tolower("'"$RE"'") cm_offset = length("'"${VIDEO_CMODE}${CM_NORM}"'") - 1 } { lin = $0 low = tolower(lin) offset = 0 while (low && match(low, re) && RLENGTH) { lin = substr(lin, 1, offset + RSTART - 1) \ "'"$VIDEO_CMODE"'" \ substr(lin, offset + RSTART, RLENGTH) \ "'"$CM_NORM"'" \ substr(lin, offset + RSTART + RLENGTH) low = substr(low, RSTART + RLENGTH) offset += RSTART + RLENGTH + cm_offset } print lin }' "$@" ;; *) echo "${0}: Internal error." >&2 exit 1 ;; esac #--