File: hawk.sh 1 #!/bin/sh 2 3 # The MIT License (MIT) 4 # 5 # Copyright (c) 2026 pacman64 6 # 7 # Permission is hereby granted, free of charge, to any person obtaining a copy 8 # of this software and associated documentation files (the "Software"), to deal 9 # in the Software without restriction, including without limitation the rights 10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 # copies of the Software, and to permit persons to whom the Software is 12 # furnished to do so, subject to the following conditions: 13 # 14 # The above copyright notice and this permission notice shall be included in 15 # all copies or substantial portions of the Software. 16 # 17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 # SOFTWARE. 24 25 26 # hawk [options...] [awk condition] [files...] 27 # 28 # 29 # Highlight (lines) satisfying an AWK condition/expression, keeping other 30 # lines the same. When not given a condition, all lines are highlighted. 31 # 32 # The handy case-insensitive shortcut options may cause this tool to fail, 33 # if the main AWK tool installed doesn't support the special IGNORECASE 34 # variable. 35 # 36 # The AWK options available only in single-dash versions are 37 # 38 # -F fs use `fs` for the field separator (the `FS` variable) 39 # -V show the AWK version installed 40 # -v var=val set a variable to a given value 41 # 42 # The other options are, available both in single and double-dash versions 43 # 44 # -h, -help show this help message 45 # -ins, -insensitive match regexes case-insensitively; fail if unsupported 46 # -tsv split fields using tabs, same as using -F "\t" 47 # 48 # -blue use a blue style 49 # -gray use a gray style 50 # -green use a green style 51 # -orange use an orange style 52 # -purple use a purple style 53 # -red use a red style 54 # 55 # -bb, -blueback use a blue-background style 56 # -grayback use a gray-background style 57 # -gb, -greenback use a green-background style 58 # -ob, -orangeback use an orange-background style 59 # -pb, -purpleback use a purple-background style 60 # -rb, -redback use a red-background style 61 62 63 case "$1" in 64 -h|--h|-help|--help) 65 awk '/^# +hawk /, /^$/ { gsub(/^# ?/, ""); print }' "$0" 66 exit 0 67 ;; 68 esac 69 70 ansi='7m' 71 command='awk' 72 if { [ -p /dev/stdout ] || [ -t 1 ]; } && [ -e /usr/bin/stdbuf ]; then 73 command='stdbuf -oL awk' 74 fi 75 76 tsv=0 77 case_insensitive=0 78 79 while [ $# -gt 0 ]; do 80 arg="$1" 81 82 if [ "${arg}" = "--" ]; then 83 shift 84 break 85 fi 86 87 case "${arg}" in 88 -F) 89 shift 90 if [ $# -eq 0 ]; then 91 printf "expected value after -F option\n" >&2 92 exit 1 93 fi 94 command="${command} -F $1" 95 shift 96 continue 97 ;; 98 99 -F*) 100 command="${command} ${arg}" 101 shift 102 continue 103 ;; 104 105 -v) 106 shift 107 if [ $# -eq 0 ]; then 108 printf "expected variable assignment after -v option\n" >&2 109 exit 1 110 fi 111 command="${command} -v $1" 112 shift 113 continue 114 ;; 115 116 -ins|--ins|-insensitive|--insensitive) 117 case_insensitive=1 118 shift 119 continue 120 ;; 121 122 -tsv|--tsv) 123 tsv=1 124 shift 125 continue 126 ;; 127 128 -blue|--blue) 129 ansi='38;2;0;95;215m' 130 shift 131 continue 132 ;; 133 134 -bb|--bb|-blueback|--blueback) 135 ansi='48;2;0;95;215m\x1b[38;2;238;238;238m' 136 shift 137 continue 138 ;; 139 140 -bold|--bold|-bolded|--bolded) 141 ansi='1m' 142 shift 143 continue 144 ;; 145 146 -dim|--dim|-faint|--faint|-gray|--gray) 147 ansi='38;2;168;168;168m' 148 shift 149 continue 150 ;; 151 152 -grayback|--grayback) 153 ansi='48;2;168;168;168m\x1b[38;2;238;238;238m' 154 shift 155 continue 156 ;; 157 158 -green|--green) 159 ansi='38;2;0;135;95m' 160 shift 161 continue 162 ;; 163 164 -gb|--gb|-greenback|--greenback) 165 ansi='48;2;0;135;95m\x1b[38;2;238;238;238m' 166 shift 167 continue 168 ;; 169 170 -inv|--inv|-inverse|--inverse|-invert|--invert|-inverted|--inverted) 171 ansi='7m' 172 shift 173 continue 174 ;; 175 176 -orange|--orange) 177 ansi='38;2;215;95;0m' 178 shift 179 continue 180 ;; 181 182 -ob|--ob|-orangeback|--orangeback) 183 ansi='48;2;215;95;0m\x1b[38;2;238;238;238m' 184 shift 185 continue 186 ;; 187 188 -purple|--purple) 189 ansi='38;2;135;95;255m' 190 shift 191 continue 192 ;; 193 194 -pb|--pb|-purpleback|--purpleback) 195 ansi='48;2;135;95;255m\x1b[38;2;238;238;238m' 196 shift 197 continue 198 ;; 199 200 -red|--red) 201 ansi='38;2;204;0;0m' 202 shift 203 continue 204 ;; 205 206 -rb|--rb|-redback|--redback) 207 ansi='48;2;204;0;0m\x1b[38;2;238;238;238m' 208 shift 209 continue 210 ;; 211 212 -strike|--strike|-striked|--striked|-stricken|--stricken) 213 ansi='9m' 214 shift 215 continue 216 ;; 217 218 -underline|--underline|-underlined|--underlined) 219 ansi='4m' 220 shift 221 continue 222 ;; 223 224 -*) 225 command="${command} ${arg}" 226 shift 227 continue 228 ;; 229 esac 230 231 break 232 done 233 234 cond="${1:-1}" 235 [ $# -gt 0 ] && shift 236 237 # show all non-existing files given 238 failed=0 239 for arg in "$@"; do 240 if [ "${arg}" = "-" ]; then 241 continue 242 fi 243 if [ ! -e "${arg}" ]; then 244 printf "no file named \"%s\"\n" "${arg}" > /dev/stderr 245 failed=1 246 fi 247 done 248 249 if [ "${failed}" -gt 0 ]; then 250 exit 2 251 fi 252 253 ci=' 254 BEGIN { 255 if (IGNORECASE == "") { 256 m = "your `awk` command lacks case-insensitive regex-matching" 257 print(m) > "/dev/stderr" 258 exit 125 259 } 260 IGNORECASE = 1 261 } 262 ' 263 if [ "${case_insensitive}" -eq 0 ]; then 264 ci='' 265 fi 266 267 src="${ci}"' 268 '"${cond}"' { 269 gsub(/\x1b\[0m/, "\x1b[0m\x1b['"${ansi}"'") 270 printf "\x1b['"${ansi}"'%s\x1b[0m%s", $0, ORS 271 next 272 } 273 1 274 ' 275 276 if [ "${tsv}" -eq 1 ]; then 277 ${command} -F "\t" "${src}" "$@" 278 else 279 ${command} "${src}" "$@" 280 fi