├── .editorconfig ├── .gitignore ├── LICENSE ├── NOTICE ├── README.md ├── deps └── lobash.bash ├── generate └── test /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | end_of_line = lf 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.{js,jsx,ts}] 12 | indent_size = 4 13 | 14 | [*.{diff,md}] 15 | trim_trailing_whitespace = false 16 | 17 | [Makefile] 18 | indent_style = tab 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | !.gitkeep 2 | colors.bash 3 | 4 | # Created by https://www.gitignore.io/api/vim,osx 5 | 6 | ### Vim ### 7 | # swap 8 | [._]*.s[a-v][a-z] 9 | [._]*.sw[a-p] 10 | [._]s[a-v][a-z] 11 | [._]sw[a-p] 12 | # session 13 | Session.vim 14 | # temporary 15 | .netrwhist 16 | *~ 17 | # auto-generated tag files 18 | tags 19 | 20 | 21 | ### OSX ### 22 | *.DS_Store 23 | .AppleDouble 24 | .LSOverride 25 | 26 | # Icon must end with two \r 27 | Icon 28 | # Thumbnails 29 | ._* 30 | # Files that might appear in the root of a volume 31 | .DocumentRevisions-V100 32 | .fseventsd 33 | .Spotlight-V100 34 | .TemporaryItems 35 | .Trashes 36 | .VolumeIcon.icns 37 | .com.apple.timemachine.donotpresent 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | 45 | # End of https://www.gitignore.io/api/vim,osx 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019-2023 ADoyle (adoyle.h@gmail.com). 2 | Some rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adoyle-h/shell-general-colors/f936531878ffd36e5e698bc6c3263267632111aa/NOTICE -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Shell General Colors 2 | 3 | A script to generate sets of shell variables (ANSI escape sequences) to control text color, boldness, underlining, blinking and other effects. 4 | 5 | Support general colors: `BLACK` `RED` `GREEN` `YELLOW` `BLUE` `PURPLE` `CYAN` `WHITE` `GREY`. 6 | 7 | Not support custom RGB color. [ansi][] is a good choice. 8 | 9 | The difference between ansi and Shell General Colors is usability. 10 | Shell General Colors provides simple variables which aim to be fast in runtime while ansi provides flexible functions. 11 | 12 | ## Preview 13 | 14 | See [Usage](#usage) and run `./test` to preview. 15 | 16 | ## Versioning 17 | 18 | Read [tags][] for verions. 19 | The versions follow the rules of [Semantic Versioning 2.0.0](http://semver.org/spec/v2.0.0.html). 20 | 21 | ## Installation 22 | 23 | ```sh 24 | # Clone this repo 25 | git clone --depth 1 https://github.com/adoyle-h/shell-general-colors.git 26 | # Copy it to somewhere in your path 27 | sudo ln -s "$PWD/generate" /usr/local/bin/shell-general-colors 28 | ``` 29 | 30 | ## Usage 31 | 32 | There are two ways to generate colors: list or map. 33 | 34 | ### Color List 35 | 36 | #### Generate Color List 37 | 38 | First, generate a colors.bash file to your project. 39 | 40 | ```sh 41 | # cd to your project 42 | # "shell-general-colors -h" to get usage 43 | shell-general-colors 44 | # Generated file: colors.bash 45 | ``` 46 | 47 | The generated file "colors.bash" will contain below codes. 48 | 49 | ```sh 50 | # General Foreground Colors 51 | BLACK='\e[30m' 52 | RED='\e[31m' 53 | GREEN='\e[32m' 54 | YELLOW='\e[33m' 55 | BLUE='\e[34m' 56 | PURPLE='\e[35m' 57 | CYAN='\e[36m' 58 | WHITE='\e[37m' 59 | GREY='\e[90m' 60 | 61 | # ... 62 | 63 | # RESET 64 | RESET_FG='\e[39m' 65 | RESET_BG='\e[49m' 66 | RESET_ALL='\e[0m' 67 | ``` 68 | 69 | #### Use Color List 70 | 71 | Then source the colors.bash file and use these variables directly. 72 | 73 | ```sh 74 | source /colors.bash 75 | 76 | echo -e "this is ${RED}red${RESET_ALL}. this is ${YELLOW}yellow${RESET_ALL}." 77 | printf 'this is %bblue%b.' "${BLUE}" "${RESET_ALL}" 78 | ``` 79 | 80 | If you want to use color variables with [here documents][]. Use [escaped variables](#export-escaped-variables). 81 | 82 | ### Color Map 83 | 84 | #### Generate Color Map 85 | 86 | First, generate a colors.bash file to your project. 87 | 88 | ```sh 89 | # cd to your project 90 | # "shell-general-colors -h" to get usage 91 | shell-general-colors --map 92 | # Generated file: colors.bash 93 | ``` 94 | 95 | **Notice**: When use `--map` option and specific output, you must pass `--` before output path. 96 | 97 | ```sh 98 | shell-general-colors --map -- colors.bash 99 | ``` 100 | 101 | The generated file "colors.bash" will contain below codes. 102 | 103 | ```sh 104 | declare -g -A colors=( 105 | 106 | # General Foreground Colors 107 | [BLACK]='\e[30m' 108 | [RED]='\e[31m' 109 | [GREEN]='\e[32m' 110 | [YELLOW]='\e[33m' 111 | [BLUE]='\e[34m' 112 | [PURPLE]='\e[35m' 113 | [CYAN]='\e[36m' 114 | [WHITE]='\e[37m' 115 | [GREY]='\e[90m' 116 | 117 | # ... 118 | 119 | # RESET 120 | [RESET_FG]='\e[39m' 121 | [RESET_BG]='\e[49m' 122 | [RESET_ALL]='\e[0m' 123 | ) 124 | ``` 125 | 126 | #### Use Color Map 127 | 128 | Then source the colors.bash file and use these variables directly. 129 | 130 | ```sh 131 | source /colors.bash 132 | 133 | RESET_ALL=${colors[RESET_ALL]} 134 | color1=RED 135 | color2=YELLOW 136 | color3=BLUE 137 | 138 | echo -e "this is ${colors[color1]}red${RESET_ALL}. this is ${colors[color2]}yellow${RESET_ALL}." 139 | printf 'this is %bblue%b.' "${colors[color3]}" "${RESET_ALL}" 140 | ``` 141 | 142 | If you want to use color variables with [here documents][]. Use [escaped variables](#export-escaped-variables). 143 | 144 | ## Advanced Usage 145 | 146 | ### Change generated file path 147 | 148 | ```sh 149 | shell-general-colors "~/colors.bash" 150 | # $output generated 151 | ``` 152 | 153 | ### Force write 154 | 155 | The script checks existed file by default. You can force write file with `-y` option. 156 | 157 | ```sh 158 | shell-general-colors -y 159 | # $output generated 160 | ``` 161 | 162 | ### Set variable prefix 163 | 164 | If `-p=` set, Add prefix `` to each name of exported variables. 165 | 166 | ```sh 167 | shell-general-colors -p C_ 168 | # C_BLACK, C_RED, C_BOLD_BLACK ... 169 | ``` 170 | 171 | ### Export escaped variables 172 | 173 | If `-e` set, export escaped variables instead of general variables. 174 | 175 | If `-e=` set, export escaped variables instead of general variables. And add suffix `` to each name of escaped variables. 176 | 177 | ```sh 178 | shell-general-colors -e _ESC 179 | # BLACK_ESC, RED_ESC, BOLD_BLACK_ESC ... 180 | ``` 181 | 182 | You can use escaped variables with [here documents][]. For example, 183 | 184 | ```sh 185 | cat < 229 | 230 | [issue]: https://github.com/adoyle-h/shell-general-colors/issues 231 | [tags]: https://github.com/adoyle-h/shell-general-colors/tags 232 | [LICENSE]: ./LICENSE 233 | [NOTICE]: ./NOTICE 234 | [ansi]: https://github.com/fidian/ansi 235 | [here documents]: http://tldp.org/LDP/abs/html/here-docs.html 236 | -------------------------------------------------------------------------------- /deps/lobash.bash: -------------------------------------------------------------------------------- 1 | # This file is generated by https://github.com/adoyle-h/lobash 2 | # Author: ADoyle 3 | # License: Apache License Version 2.0 4 | # Version: 0.4.0 5 | # Prefix: l. 6 | # Bash Minimum Version: 4.0 7 | # Included Modules: ask lower_case parse_params start_with match match_list 8 | # 9 | # Note: Not all features are supported in Bash 4.0. 10 | # Please read this link: https://github.com/adoyle-h/lobash/blob/develop/doc/with-lower-version-bash.md 11 | 12 | ######################## Lobash Internals ######################## 13 | 14 | _lobash.0_4_0_13829724_25401_detect_os() { 15 | local kernel_name 16 | kernel_name="$(uname -s)" 17 | 18 | case "$kernel_name" in 19 | "Darwin") echo MacOS ;; 20 | "SunOS") echo Solaris ;; 21 | "Haiku") echo Haiku ;; 22 | "MINIX") echo MINIX ;; 23 | "AIX") echo AIX ;; 24 | "IRIX"*) echo IRIX ;; 25 | "FreeMiNT") echo FreeMiNT ;; 26 | "Linux" | "GNU"*) echo Linux ;; 27 | *"BSD" | "DragonFly" | "Bitrig") echo BSD ;; 28 | "CYGWIN"* | "MSYS"* | "MINGW"*) echo Windows ;; 29 | *) echo Unknown_OS "$kernel_name" ;; 30 | esac 31 | } 32 | 33 | _lobash.0_4_0_13829724_25401_is_bash() { 34 | [[ -n "${BASH_VERSION:-}" ]] 35 | } 36 | 37 | 38 | 39 | [[ -n ${_LOBASH_0_4_0_13829724_25401_INTERNAL_FUNC_PREFIX:-} ]] && return 40 | 41 | readonly _LOBASH_0_4_0_13829724_25401_INTERNAL_FUNC_PREFIX=_lobash. 42 | readonly _LOBASH_0_4_0_13829724_25401_INTERNAL_CONST_PREFIX=_LOBASH_ 43 | readonly _LOBASH_0_4_0_13829724_25401_PRIVATE_FUNC_PREFIX=_l. 44 | readonly _LOBASH_0_4_0_13829724_25401_PRIVATE_CONST_PREFIX=_L_ 45 | readonly _LOBASH_0_4_0_13829724_25401_PUBLIC_FUNC_PREFIX=l. 46 | readonly _LOBASH_0_4_0_13829724_25401_PUBLIC_CONST_PREFIX=L_ 47 | _LOBASH_0_4_0_13829724_25401_PREFIX=l. 48 | _LOBASH_0_4_0_13829724_25401_PUBLIC_DEPTH=1 49 | _LOBASH_0_4_0_13829724_25401_MIN_BASHVER=4.0 50 | 51 | readonly _LOBASH_0_4_0_13829724_25401_OS=$(_lobash.0_4_0_13829724_25401_detect_os) 52 | 53 | _lobash.0_4_0_13829724_25401_check_os() { 54 | if [[ ! $_LOBASH_0_4_0_13829724_25401_OS =~ ^(Linux|MacOS|BSD)$ ]]; then 55 | echo "Not support current system: $_LOBASH_0_4_0_13829724_25401_OS" >&2 56 | return 5 57 | fi 58 | } 59 | 60 | _lobash.0_4_0_13829724_25401_check_shell() { 61 | if ! _lobash.0_4_0_13829724_25401_is_bash; then 62 | echo 'Lobash only work in Bash.' >&2 63 | return 6 64 | fi 65 | } 66 | 67 | _lobash.0_4_0_13829724_25401_check_supported_bash_version() { 68 | local info 69 | read -r -d '.' -a info <<< "$_LOBASH_0_4_0_13829724_25401_MIN_BASHVER" 70 | if (( BASH_VERSINFO[0] < info[0] )) \ 71 | || ( (( BASH_VERSINFO[0] == info[0] )) && (( BASH_VERSINFO[1] < info[1] )) ); then 72 | echo "Bash $BASH_VERSION is not supported. Upgrade your Bash to $_LOBASH_0_4_0_13829724_25401_MIN_BASHVER or higher version." >&2 73 | return 7 74 | fi 75 | } 76 | 77 | _lobash.0_4_0_13829724_25401_check_support() { 78 | _lobash.0_4_0_13829724_25401_check_os 79 | _lobash.0_4_0_13829724_25401_check_shell 80 | # _lobash.0_4_0_13829724_25401_check_supported_bash_version 81 | } 82 | 83 | _lobash.0_4_0_13829724_25401_check_support 84 | 85 | _lobash.0_4_0_13829724_25401_dirname() { 86 | local str=${1:-} 87 | [[ $str == '/' ]] && echo '/' && return 0 88 | [[ $str =~ ^'../' ]] && echo '.' && return 0 89 | [[ ! $str =~ / ]] && echo '.' && return 0 90 | 91 | printf '%s\n' "${str%/*}" 92 | } 93 | 94 | _lobash.0_4_0_13829724_25401_with_IFS() { 95 | local IFS=$1 96 | shift 97 | eval "$@" 98 | } 99 | _LOBASH_0_4_0_13829724_25401_PREFIX=l. 100 | _LOBASH_0_4_0_13829724_25401_PUBLIC_DEPTH=2 101 | _LOBASH_0_4_0_13829724_25401_MIN_BASHVER=4.0 102 | 103 | ######################## Private Methods ######################## 104 | 105 | 106 | l.0_4_0_13829724_25401_ask() { 107 | local msg=$1 108 | local default=${2:-Y} 109 | local prompt 110 | if [[ $default == Y ]]; then 111 | default=YES 112 | prompt='([Y]es/No)' 113 | elif [[ $default == N ]]; then 114 | default=NO 115 | prompt='(Yes/[N]o)' 116 | else 117 | echo "Invalid argument 'default'. Valid value is 'Y' and 'N'. Current=${default}" >&2 118 | return 3 119 | fi 120 | 121 | local answer 122 | read -rp "$msg $prompt " answer 123 | 124 | answer=$(l.0_4_0_13829724_25401_lower_case "$answer") 125 | if [[ $answer =~ ^ye?s?$ ]]; then 126 | echo YES 127 | elif [[ $answer =~ ^no?$ ]]; then 128 | echo NO 129 | elif [[ $answer == '' ]]; then 130 | echo "$default" 131 | else 132 | echo 'Invalid Answer' 133 | fi 134 | 135 | return 0 136 | } 137 | 138 | 139 | 140 | l.0_4_0_13829724_25401_lower_case() { 141 | local str=${1:-} 142 | printf '%s\n' "${str,,}" 143 | } 144 | 145 | 146 | _l.0_4_0_13829724_25401_parse_single_hyphen_options() { 147 | local param=${1#-} 148 | 149 | # Possible Formats: 150 | # -a 3 or -abc 3 151 | # -a or -a3 or -abc3 152 | # -a=3 or -abc=3 153 | # -a= or -abc= 154 | 155 | local -a matches=() 156 | local k letters 157 | l.0_4_0_13829724_25401_match_list "$param" '^(.+)=(.*)$' matches 158 | if (( ${#matches[@]} == 2 )); then 159 | # -a=3 or -abc=3 160 | # -a= or -abc= 161 | letters=${matches[0]} 162 | $add_opt "${letters: -1:1}" "${matches[1]}" 163 | 164 | letters=${letters:0:$(( ${#letters} - 1 ))} 165 | for (( k = 0; k < ${#letters} ; k++ )); do 166 | $add_opt "${letters:$k:1}" true 167 | done 168 | return 169 | fi 170 | 171 | if (( $# == 2 )); then 172 | # -a 3 or -abc 3 173 | $add_opt "${param: -1:1}" "$2" 174 | (( i+=1 )) 175 | 176 | letters=${param:0:$(( ${#param} - 1 ))} 177 | for (( k = 0; k < ${#letters}; k++ )); do 178 | $add_opt "${letters:$k:1}" true 179 | done 180 | return 181 | else 182 | # -a or -a3 or -abc3 183 | for (( k = 0; k < ${#param}; k++ )); do 184 | $add_opt "${param:$k:1}" true 185 | done 186 | return 187 | fi 188 | 189 | echo "Unknown option: -$param" >&2 190 | return 4 191 | } 192 | 193 | _l.0_4_0_13829724_25401_parse_double_hyphen_options() { 194 | local param=${1#--} 195 | 196 | local -a matches=() 197 | l.0_4_0_13829724_25401_match_list "$param" '^(.+)=(.*)' matches 198 | 199 | if (( ${#matches[@]} == 2 )); then 200 | $add_opt "${matches[0]}" "${matches[1]}" 201 | else 202 | local key 203 | key=$(l.0_4_0_13829724_25401_match "$param" '^no-(.+)') 204 | if [[ -n $key ]]; then 205 | $add_opt "${key}" false 206 | else 207 | key=$param 208 | if (( $# == 2 )); then 209 | $add_opt "${key}" "$2" 210 | else 211 | $add_opt "${key}" true 212 | fi 213 | fi 214 | fi 215 | } 216 | 217 | _l.0_4_0_13829724_25401_parse_params_add_none() { 218 | return 0 219 | } 220 | 221 | _l.0_4_0_13829724_25401_parse_params_add_opt() { 222 | eval "$opts_name[\$1]=\"\$2\"" 223 | } 224 | 225 | _l.0_4_0_13829724_25401_parse_params_add_arg() { 226 | eval "$args_name+=(\"\$1\")" 227 | } 228 | 229 | l.0_4_0_13829724_25401_parse_params() { 230 | if (( $# < 2 )); then 231 | echo "parse_params: programming error. Parameters cannot less than 2." >&2 232 | return 3 233 | fi 234 | 235 | local add_opt add_arg 236 | if [[ $1 != _ ]]; then 237 | local opts_name=$1 238 | add_opt=_l.0_4_0_13829724_25401_parse_params_add_opt 239 | else 240 | add_opt=_l.0_4_0_13829724_25401_parse_params_add_none 241 | fi 242 | 243 | if [[ $2 != _ ]]; then 244 | local args_name=$2 245 | add_arg=_l.0_4_0_13829724_25401_parse_params_add_arg 246 | else 247 | add_arg=_l.0_4_0_13829724_25401_parse_params_add_none 248 | fi 249 | 250 | local param_size=$# 251 | local param 252 | local rest_is_args=false 253 | for (( i = 3; i <= param_size; i++ )); do 254 | param=${!i} 255 | 256 | if l.0_4_0_13829724_25401_start_with "$param" '-'; then 257 | local j=$(( i+1 )) 258 | local f 259 | 260 | if [[ $param == '--' ]]; then 261 | rest_is_args=true 262 | (( i+=1 )) 263 | break; 264 | fi 265 | 266 | if l.0_4_0_13829724_25401_start_with "$param" '--'; then 267 | f=_l.0_4_0_13829724_25401_parse_double_hyphen_options 268 | else 269 | f=_l.0_4_0_13829724_25401_parse_single_hyphen_options 270 | fi 271 | 272 | if (( j > param_size )) ; then 273 | $f "$param" 274 | else 275 | local next=${!j} 276 | if l.0_4_0_13829724_25401_start_with "$next" '-' ; then 277 | $f "$param" 278 | else 279 | $f "$param" "$next" 280 | fi 281 | fi 282 | else 283 | $add_arg "$param" 284 | fi 285 | done 286 | 287 | if [[ $rest_is_args == true ]]; then 288 | for (( ; i <= param_size; i++ )); do 289 | $add_arg "${!i}" 290 | done 291 | fi 292 | } 293 | 294 | 295 | l.0_4_0_13829724_25401_start_with() { 296 | [[ $2${1##"$2"} == "$1" ]] 297 | } 298 | 299 | 300 | l.0_4_0_13829724_25401_match() { 301 | [[ ${3:-} == 0 ]] && echo "index cannot be 0" >&2 && return 3 302 | 303 | if [[ $1 =~ $2 ]]; then 304 | if (( ${#BASH_REMATCH[@]} > 1 )); then 305 | printf '%s\n' "${BASH_REMATCH[${3:-1}]}" 306 | else 307 | echo '' 308 | fi 309 | else 310 | echo '' 311 | fi 312 | } 313 | 314 | 315 | l.0_4_0_13829724_25401_match_list() { 316 | (( $# != 3 )) && echo "wrong parameters" >&2 && return 3 317 | 318 | local output_array_name=$3 319 | 320 | if [[ $1 =~ $2 ]]; then 321 | local len=${#BASH_REMATCH[@]} 322 | local i 323 | if (( len > 1 )); then 324 | for (( i = 1; i < len; i++ )); do 325 | eval "${output_array_name}+=( \"\${BASH_REMATCH[$i]}\" )" 326 | done 327 | fi 328 | fi 329 | } 330 | 331 | ######################## Public Methods ######################## 332 | l.ask() { l.0_4_0_13829724_25401_ask "$@"; } 333 | l.lower_case() { l.0_4_0_13829724_25401_lower_case "$@"; } 334 | l.parse_params() { l.0_4_0_13829724_25401_parse_params "$@"; } 335 | l.start_with() { l.0_4_0_13829724_25401_start_with "$@"; } 336 | l.match() { l.0_4_0_13829724_25401_match "$@"; } 337 | l.match_list() { l.0_4_0_13829724_25401_match_list "$@"; } 338 | -------------------------------------------------------------------------------- /generate: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | (shopt -p inherit_errexit &>/dev/null) && shopt -s inherit_errexit 7 | 8 | # https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences 9 | # ╔══════════╦════════════════════════════════╦═════════════════════════════════════════════════════════════════════════╗ 10 | # ║ Code ║ Effect ║ Note ║ 11 | # ╠══════════╬════════════════════════════════╬═════════════════════════════════════════════════════════════════════════╣ 12 | # ║ 0 ║ Reset / Normal ║ all attributes off ║ 13 | # ║ 1 ║ Bold or increased intensity ║ ║ 14 | # ║ 2 ║ Faint (decreased intensity) ║ Not widely supported. ║ 15 | # ║ 3 ║ Italic ║ Not widely supported. Sometimes treated as inverse. ║ 16 | # ║ 4 ║ Underline ║ ║ 17 | # ║ 5 ║ Slow Blink ║ less than 150 per minute ║ 18 | # ║ 6 ║ Rapid Blink ║ MS-DOS ANSI.SYS; 150+ per minute; not widely supported ║ 19 | # ║ 7 ║ [[reverse video]] ║ swap foreground and background colors ║ 20 | # ║ 8 ║ Conceal ║ Not widely supported. ║ 21 | # ║ 9 ║ Crossed-out ║ Characters legible, but marked for deletion. Not widely supported. ║ 22 | # ║ 10 ║ Primary(default) font ║ ║ 23 | # ║ 11–19 ║ Alternate font ║ Select alternate font `n-10` ║ 24 | # ║ 20 ║ Fraktur ║ hardly ever supported ║ 25 | # ║ 21 ║ Bold off or Double Underline ║ Bold off not widely supported; double underline hardly ever supported. ║ 26 | # ║ 22 ║ Normal color or intensity ║ Neither bold nor faint ║ 27 | # ║ 23 ║ Not italic, not Fraktur ║ ║ 28 | # ║ 24 ║ Underline off ║ Not singly or doubly underlined ║ 29 | # ║ 25 ║ Blink off ║ ║ 30 | # ║ 27 ║ Inverse off ║ ║ 31 | # ║ 28 ║ Reveal ║ conceal off ║ 32 | # ║ 29 ║ Not crossed out ║ ║ 33 | # ║ 30–37 ║ Set foreground color ║ See color table below ║ 34 | # ║ 38 ║ Set foreground color ║ Next arguments are `5;n` or `2;r;g;b`, see below ║ 35 | # ║ 39 ║ Default foreground color ║ implementation defined (according to standard) ║ 36 | # ║ 40–47 ║ Set background color ║ See color table below ║ 37 | # ║ 48 ║ Set background color ║ Next arguments are `5;n` or `2;r;g;b`, see below ║ 38 | # ║ 49 ║ Default background color ║ implementation defined (according to standard) ║ 39 | # ║ 51 ║ Framed ║ ║ 40 | # ║ 52 ║ Encircled ║ ║ 41 | # ║ 53 ║ Overlined ║ ║ 42 | # ║ 54 ║ Not framed or encircled ║ ║ 43 | # ║ 55 ║ Not overlined ║ ║ 44 | # ║ 60 ║ ideogram underline ║ hardly ever supported ║ 45 | # ║ 61 ║ ideogram double underline ║ hardly ever supported ║ 46 | # ║ 62 ║ ideogram overline ║ hardly ever supported ║ 47 | # ║ 63 ║ ideogram double overline ║ hardly ever supported ║ 48 | # ║ 64 ║ ideogram stress marking ║ hardly ever supported ║ 49 | # ║ 65 ║ ideogram attributes off ║ reset the effects of all of 60-64 ║ 50 | # ║ 90–97 ║ Set bright foreground color ║ aixterm (not in standard) ║ 51 | # ║ 100–107 ║ Set bright background color ║ aixterm (not in standard) ║ 52 | # ╚══════════╩════════════════════════════════╩═════════════════════════════════════════════════════════════════════════╝ 53 | 54 | readlinkf() { # Modified from https://github.com/ko1nksm/readlinkf 55 | [ "${1:-}" ] || return 1 56 | 57 | CDPATH='' # to avoid changing to an unexpected directory 58 | local max_symlinks=40 59 | local link 60 | local target=$1 61 | 62 | [ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes 63 | [ -d "${target:-/}" ] && target="$target/" 64 | 65 | cd -P . 2>/dev/null || return 1 66 | while [ "$max_symlinks" -ge 0 ] && max_symlinks=$((max_symlinks - 1)); do 67 | if [ ! "$target" = "${target%/*}" ]; then 68 | case $target in 69 | /*) cd -P "${target%/*}/" 2>/dev/null || break ;; 70 | *) cd -P "./${target%/*}" 2>/dev/null || break ;; 71 | esac 72 | target=${target##*/} 73 | fi 74 | 75 | if [ ! -L "$target" ]; then 76 | target="${PWD%/}${target:+/}${target}" 77 | printf '%s\n' "${target:-/}" 78 | return 0 79 | fi 80 | 81 | link=$(ls -dl -- "$target" 2>/dev/null) || break 82 | target=${link#*" $target -> "} 83 | done 84 | return 1 85 | } 86 | 87 | SCRIPT_PATH="$(readlinkf "$0")" 88 | SCRIPT_DIR=$(dirname "$SCRIPT_PATH") 89 | 90 | # shellcheck source=deps/lobash.bash 91 | source "$SCRIPT_DIR"/deps/lobash.bash 92 | 93 | declare -A opts=() 94 | declare -a args=() 95 | 96 | usage() { 97 | cat <] [-e=] [-a] [=./colors.bash] 99 | 100 | Options: 101 | -h, --help Show usage. 102 | -y If set, force write the colors file 103 | -p= If set, Add prefix '' to each name of exported variables. 104 | -e, -e= If set, export escaped variables instead of general variables. And add suffix '' to each name of escaped variables. 105 | -a If set, export both general variables and escaped variables. 106 | --map If set, export a colors map instead of color lists 107 | EOF 108 | } 109 | 110 | init() { 111 | l.parse_params opts args "$@" 112 | 113 | if [[ ${opts[h]:-} == true ]] || [[ ${opts[help]:-} == true ]]; then 114 | usage 115 | exit 0 116 | fi 117 | 118 | declare -g TARGET EXPORT_GENERAL=false EXPORT_ESCAPED=false 119 | 120 | TARGET=${args[0]:-colors.bash} 121 | if [[ -d $TARGET ]]; then 122 | TARGET=${TARGET}/colors.bash 123 | fi 124 | 125 | # User Specified Environment Variables 126 | PREFIX=${opts[p]:-} 127 | ESCAPED_SUFFIX=${opts[e]:-} 128 | 129 | if [[ -n ${opts[a]:-} ]]; then 130 | EXPORT_GENERAL=true 131 | EXPORT_ESCAPED=true 132 | if [[ -z $ESCAPED_SUFFIX ]] || [[ $ESCAPED_SUFFIX == true ]]; then ESCAPED_SUFFIX='_ESC'; fi 133 | else 134 | if [[ -n $ESCAPED_SUFFIX ]]; then 135 | if [[ $ESCAPED_SUFFIX == true ]]; then ESCAPED_SUFFIX=''; fi 136 | EXPORT_ESCAPED=true 137 | else 138 | EXPORT_GENERAL=true 139 | fi 140 | fi 141 | 142 | declare -g BASE_COLORS=( 143 | BLACK 144 | RED 145 | GREEN 146 | YELLOW 147 | BLUE 148 | PURPLE 149 | CYAN 150 | WHITE 151 | ) 152 | 153 | declare -ga BG_COLORS 154 | local COLOR 155 | for COLOR in "${BG_COLORS[@]}"; do 156 | BG_COLORS+=("$COLOR") 157 | done 158 | 159 | local -a attrs=( 160 | RESET 0 161 | BOLD 1 162 | # FAINT 2 163 | # ITALIC 3 164 | UNDERLINE 4 165 | BLINK 5 166 | # RAPID_BLINK 6 167 | REVERSE 7 168 | # CONCEAL 8 169 | # CROSSED_OUT 9 170 | BLACK 30 171 | RED 31 172 | GREEN 32 173 | YELLOW 33 174 | BLUE 34 175 | PURPLE 35 176 | CYAN 36 177 | WHITE 37 178 | RESET_FG 39 179 | BLACK_BG 40 180 | RED_BG 41 181 | GREEN_BG 42 182 | YELLOW_BG 43 183 | BLUE_BG 44 184 | PURPLE_BG 45 185 | CYAN_BG 46 186 | WHITE_BG 47 187 | RESET_BG 49 188 | GREY 90 189 | ) 190 | 191 | local len=${#attrs[@]} 192 | local i 193 | for (( i = 0; i < "$len"; i+=2 )); do 194 | declare -g "${attrs[$i]}" 195 | read -r "${attrs[$i]}" <<< "${attrs[$((i+1))]}" 196 | done 197 | } 198 | 199 | _BG() { 200 | printf '%s' $(($1 + 10)) 201 | } 202 | 203 | _BRIGHT() { 204 | printf '%s' $(($1 + 60)) 205 | } 206 | 207 | # Valid Formats: 208 | # "${ESC}[{FG_COLOR}m" 209 | # "${ESC}[{FG_COLOR};{ATTR}m" 210 | # "${ESC}[{FG_COLOR};{BG_COLOR};{ATTR}m" 211 | 212 | ansi() { 213 | local ESC='\e' 214 | # local ESC=$'\033' 215 | # local ESC=$'\x1B' 216 | # local ESC=$'\u001b' 217 | 218 | # Control Sequence Introducer 219 | local CSI="${ESC}[" 220 | # Operating System Command 221 | local OSC="${ESC}]" 222 | # String Terminator 223 | local ST="${ESC}\\" 224 | 225 | local c name 226 | local str='' 227 | for c in "$@"; do 228 | if [[ $c =~ ^[0-9]+$ ]]; then 229 | str="${str};${c}" 230 | else 231 | name=${c} 232 | str="${str};${!name}" 233 | fi 234 | done 235 | 236 | printf '%s%sm' "$CSI" "${str:1}" 237 | } 238 | 239 | safe_rm() { 240 | local path=$1 241 | if [[ $path == / ]]; then 242 | echo "Dangerous! Do not rm $path" 243 | return 3 244 | fi 245 | 246 | if [[ $(dirname "$path") == / ]]; then 247 | echo "Dangerous! Do not rm $path" 248 | return 3 249 | fi 250 | 251 | rm -rf "$path" 252 | } 253 | 254 | clean() { 255 | if [[ ${opts[y]:-} != 'true' ]]; then 256 | if [[ -f $TARGET ]]; then 257 | local answer 258 | answer=$(l.ask "Existed file: ${TARGET}. Overwrite it?" N) 259 | echo "$answer" 260 | case $answer in 261 | YES ) 262 | ;; 263 | * ) 264 | echo "Not overwrite it. No color file will be generated." 265 | exit 0 266 | ;; 267 | esac 268 | fi 269 | fi 270 | 271 | safe_rm "$TARGET" 272 | } 273 | 274 | write() { 275 | printf -- '%s\n' "$*" >> "$TARGET" 276 | } 277 | 278 | write_color() { 279 | if [[ $EXPORT_GENERAL == true ]]; then 280 | printf -- "%s='%s'\n" "${PREFIX}$1" "$2" >> "$TARGET" 281 | fi 282 | 283 | if [[ $EXPORT_ESCAPED == true ]]; then 284 | printf -- "%s=$'%s'\n" "${PREFIX}$1${ESCAPED_SUFFIX}" "$2" >> "$TARGET" 285 | fi 286 | } 287 | 288 | write_comment_title() { 289 | printf -- '\n# %s\n' "$*" >> "$TARGET" 290 | } 291 | 292 | general_colors() { 293 | local color_name_prefix=${1:+$1_} 294 | (( $# > 0 )) && shift 295 | 296 | local colorNum op 297 | for color in "${BASE_COLORS[@]}"; do 298 | colorNum=${!color} 299 | local ops=() 300 | 301 | for op in "$@"; do 302 | if [[ $op =~ ^_ ]]; then 303 | if [[ $op == _BRIGHT ]]; then 304 | colorNum="$($op "${colorNum}")" 305 | else 306 | ops+=( "$($op "${colorNum}")" ) 307 | fi 308 | else 309 | ops+=( "$op" ) 310 | fi 311 | done 312 | 313 | write_color "${color_name_prefix}$color" "$(ansi "$colorNum" "${ops[@]}")" 314 | done 315 | } 316 | 317 | general_colors_list() { 318 | write_comment_title 'General Foreground Colors' 319 | general_colors 320 | write_color GREY "$(ansi GREY)" 321 | 322 | write_comment_title 'General Background Colors' 323 | general_colors BG _BG 324 | write_color BG_GREY "$(ansi "$(_BG "$GREY")")" 325 | 326 | write_comment_title 'BOLD' 327 | general_colors BOLD BOLD 328 | write_color BOLD_GREY "$(ansi BOLD GREY)" 329 | 330 | write_comment_title 'UNDERLINE' 331 | general_colors UL UNDERLINE 332 | write_color UL_GREY "$(ansi UNDERLINE GREY)" 333 | 334 | write_comment_title 'BLINK' 335 | general_colors BLK BLINK 336 | write_color BLK_GREY "$(ansi BLINK GREY)" 337 | 338 | write_comment_title 'REVERSE' 339 | general_colors REV REVERSE 340 | write_color REV_GREY "$(ansi REVERSE GREY)" 341 | 342 | write_comment_title 'BRIGHT' 343 | general_colors BRI _BRIGHT 344 | 345 | write_comment_title 'BRIGHT & BOLD' 346 | general_colors BRI_BOLD _BRIGHT BOLD 347 | 348 | write_comment_title 'RESET' 349 | write_color "RESET_FG" "$(ansi RESET_FG)" 350 | write_color "RESET_BG" "$(ansi RESET_BG)" 351 | write_color "RESET_ALL" "$(ansi RESET)" 352 | } 353 | 354 | # shellcheck disable=2120 355 | general_colors_map() { 356 | write_comment_title() { 357 | printf -- '\n # %s\n' "$*" >> "$TARGET" 358 | } 359 | 360 | write_color() { 361 | if [[ $EXPORT_GENERAL == true ]]; then 362 | printf -- " [%s]='%s'\n" "$1" "$2" >> "$TARGET" 363 | fi 364 | 365 | if [[ $EXPORT_ESCAPED == true ]]; then 366 | printf -- " [%s]=$'%s'\n" "$1${ESCAPED_SUFFIX}" "$2" >> "$TARGET" 367 | fi 368 | } 369 | 370 | write "" 371 | write "declare -g -A ${PREFIX}colors=(" 372 | general_colors_list 373 | write ')' 374 | } 375 | 376 | generate() { 377 | write '# This file is generated by https://github.com/adoyle-h/shell-general-colors' 378 | write "# Command: shell-general-colors${*:+ $*}" 379 | write '# Author: ADoyle ' 380 | write '# License: BSD 3-clause License' 381 | write '# Attentions: GREY may not work in some shells' 382 | 383 | if [[ ${opts[map]:-} == true ]]; then 384 | general_colors_map 385 | else 386 | general_colors_list 387 | fi 388 | 389 | echo "Generated file: $TARGET" 390 | } 391 | 392 | init "$@" 393 | clean 394 | generate "$@" 395 | -------------------------------------------------------------------------------- /test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | set -o errtrace 7 | (shopt -p inherit_errexit &>/dev/null) && shopt -s inherit_errexit 8 | 9 | readonly SCRIPT_DIR="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" 10 | 11 | PREFIX=test_ 12 | 13 | colors=( 14 | BLACK 15 | RED 16 | GREEN 17 | YELLOW 18 | BLUE 19 | PURPLE 20 | CYAN 21 | WHITE 22 | GREY 23 | ) 24 | 25 | print_colors() { 26 | local prefix=${1:-} 27 | local key 28 | for c in "${colors[@]}"; do 29 | key="${PREFIX}${prefix}${c}" 30 | [[ ! -v $key ]] && continue 31 | echo -e "- ${!key}${prefix}${c}${test_RESET_ALL}" 32 | done 33 | } 34 | 35 | print_colors_map() { 36 | local prefix=${1:-} 37 | local key 38 | for c in "${colors[@]}"; do 39 | key="${prefix}${c}" 40 | # shellcheck disable=2154 41 | [[ -z ${test_colors[$key]:-} ]] && continue 42 | echo -e "- ${test_colors[$key]}${prefix}${c}${test_colors[RESET_ALL]}" 43 | done 44 | } 45 | 46 | echo '------ Color List ------' 47 | 48 | "$SCRIPT_DIR"/generate -y -a -p $PREFIX 49 | # shellcheck source=colors.bash 50 | source "$SCRIPT_DIR"/colors.bash 51 | 52 | printf '\n%s\n' '[General Foreground Colors]' 53 | print_colors 54 | printf '\n%s\n' '[General Background Colors]' 55 | print_colors BG_ 56 | printf '\n%s\n' '[Bold Colors]' 57 | print_colors BOLD_ 58 | printf '\n%s\n' '[Underline Colors]' 59 | print_colors UL_ 60 | printf '\n%s\n' '[Blink Colors]' 61 | print_colors BLK_ 62 | printf '\n%s\n' '[Reverse Colors]' 63 | print_colors REV_ 64 | printf '\n%s\n' '[Bright Colors]' 65 | print_colors BRI_ 66 | printf '\n%s\n' '[Bright & Bold Colors]' 67 | print_colors BRI_BOLD_ 68 | 69 | printf '\n\n%s\n' '------ Color Map ------' 70 | 71 | "$SCRIPT_DIR"/generate -y -a --map -p $PREFIX 72 | # shellcheck source=colors.bash 73 | source "$SCRIPT_DIR"/colors.bash 74 | 75 | printf '\n%s\n' '[General Foreground Colors]' 76 | print_colors_map 77 | printf '\n%s\n' '[General Background Colors]' 78 | print_colors_map BG_ 79 | printf '\n%s\n' '[Bold Colors]' 80 | print_colors_map BOLD_ 81 | printf '\n%s\n' '[Underline Colors]' 82 | print_colors_map UL_ 83 | printf '\n%s\n' '[Blink Colors]' 84 | print_colors_map BLK_ 85 | printf '\n%s\n' '[Reverse Colors]' 86 | print_colors_map REV_ 87 | printf '\n%s\n' '[Bright Colors]' 88 | print_colors_map BRI_ 89 | printf '\n%s\n' '[Bright & Bold Colors]' 90 | print_colors_map BRI_BOLD_ 91 | --------------------------------------------------------------------------------