Ecos — Echo with millions of colors (Bash package)
Below you find the latest version of (1) the package's README and (2) its main source file.
For the git repository and issue tracker, see the project's page on sr.ht.
For more packages, see Software.
Colors and terminals
Let's talk about adding some colors to that black screen you spend all day staring at.
Adding a bit of color
If you want to add color to text that shows up in your terminal, you'd normally do something like this:
echo -e '\e[1;31mHello\e[1;32m, \e[1;33mworld\e[1;35m!\e[0m'
Soon you get tired and define the colors. Then it becomes something like this:
echo -e "${BOLD_RED}Hello${BOLD_GREEN}, ${BOLD_YELLOW}world${BOLD_MAGENTA}!${NOCOLOR}"
which is longer, but definitely more readable.
With ecos, you'd do just this:
ecos 1/red Hello /green ", " /yellow world /magenta !
Isn't that much better than the previous two?
Yet one day you wake up wondering:
- Why have you been limiting yourself to eight (or sixteen-ish) colors as if we were still in the 80s?
- Your computer can show you high-resolution movies, with millions of colors. So why is your terminal skimping on them?
Why has it been offering you just a meagre mix of primary colors — of the sort you'd only see in printer tests and in tacky coupons of fast-food joints? - Couldn't you have a few more colors?
Perhaps a purple, and an orange, and a few non–eye-watering varieties of cyan and magenta?
Adding more bits
Long story short, yes, you could.
You can have 24 bits of colors in your terminal. That's 224 = 16 777 216 colors to choose from.
There's a snag, though: typing them can be even more awkward:
echo -e '\e[1;38;2;255;165;0mOrange!\e[0;38;2;0;139;139m and \e[1;38;2;160;32;240mPurple!\e[0m'
But ecos can also deal with those:
ecos 1.orange Orange! 0.cyan4 ' and ' 1.purple Purple!
All X11 color names are available — more than 500 colors that you can call by name.
Entering colors
You now do the math and realize that the number 500 is a bit smaller than 16 777 216. Alas, we can't name all these colors.
But that's ok: you can enter any of them as RGB combinations, either as decimal triplets:
ecos .120.201.122 nameless-green
or as hex:
ecos .#78c97a nameless-green
And adding a background color is as simple as:
ecos .#78c97a_.#873685 nameless-green-on-nameless-purple ecos 1.#78c97a_.#873685 bold-nameless-green-on-nameless-purple
Time to show you a ton of examples.
Examples
These come straight from examples-ecos, which you can run locally. Try:
# make it executable chmod +x ./examples-ecos # help ./examples-ecos -h # see them all ./examples-ecos # pick some option ./examples-ecos ov # ov|op|ef|4|8|24 # select from a menu ./examples-ecos -i
Let's see them all.
Note
Besides colors, you can apply effects to text, such as bold, italic, and underline. The examples show these as well.
Even though some of the effects mentioned below (notably blink and double underline) might not appear in your browser, they'd likely show alright in your terminal.
Overview
[Overview]
# Usage:
# examples-ecos
# examples-ecos [-i|ov|op|ef|4|8|24]
#
# Overview: common syntax for colors.
#
# Syntax: ecos <effect><depth><color>_<effect><depth><color> <string1> …
#
# - That's foreground_background. Either can be omitted.
# - Effects are passed through zero or more comma-separated numbers.
# - There're many effects; the most common are 0 (reset) and 1 (bold).
# - <depth> is one of [/:.] for 4, 8, 24-bits respectively.
# - You can use different depths for foreground and background, if you want.
# - <color>'s format depends on the depth.
#
# These should give you some idea:
ecos 1/2_/blue "Bold G on B" 0 ", " _1:208 "bold orange bg" 0 ", " .orange orange
⇒ Bold G on B, bold orange bg, orange
ecos .lightyellow1_.#f00 "lightyellow1 on red" 0 ", " .0.238.118 springgreen2
⇒ lightyellow1 on red, springgreen2
ecos 1,3.#ff4500_.#fff5ee "bold italic orangered1 on seashell"
⇒ bold italic orangered1 on seashell
ecos 1,3.orangered1_.seashell "bold italic orangered1 on seashell"
⇒ bold italic orangered1 on seashell
# And there's a good self-color-testing option with -s:
ecos -s 1/red 0/blue 1:208 3.#f00 .#c9fe02 1.#ff40a7 .200.27.140 1.orange1_.navy
⇒ 1/red 0/blue 1:208 3.#f00 .#c9fe02 1.#ff40a7 .200.27.140 1.orange1_.navy
# See "Options" (op) for command line options.
# See "Effects" (ef) for common effects on color.
# See each depth (4, 8, 24) for more examples.
Options
[Options]
# These are the available command line options.
#
# - REGULAR mode
# With or without newline:
ecos 1/blue "Regular mode."
⇒ Regular mode.
ecos -n 1/blue "Suppress trailing newline; you can add one with 0 '\n'"
⇒ Suppress trailing newline; you can add one with 0 '\n'
# With or without between-pairs reset of effects:
# (Note that effects accumulate in the latter. This is the default.)
ecos -r 1.red Hello 3.green There 4.skyblue3 World
⇒ HelloThereWorld
ecos 1.red Hello 3.green There 4.skyblue3 World
⇒ HelloThereWorld
#┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈
# - SELF mode: use -s to self-colorize. (Between-pairs reset is implied.)
ecos -s i/blue :208 1.orange_.navy 53.skyblue1
⇒ i/blue :208 1.orange_.navy 53.skyblue1
#┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈
# - EFFECT mode: use -e to showcase common effects applied to these colors.
ecos -e .skyblue1 /green :200
⇒
0.skyblue1 0/green 0:200
1.skyblue1 1/green 1:200
2.skyblue1 2/green 2:200
3.skyblue1 3/green 3:200
4.skyblue1 4/green 4:200
5.skyblue1 5/green 5:200
7.skyblue1 7/green 7:200
9.skyblue1 9/green 9:200
53.skyblue1 53/green 53:200
21.skyblue1 21/green 21:200
#┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈
# - OPEN mode: use -o to turn color on — commands after it will inherit it.
# To stop it: use -O to turn color off — equivalent to: ecos -n 0 "".
ecos -o 1,3/blue; echo -n Hello; echo " there!"
⇒ Hello there!
ecos -o 1,3.orange; printf Hello; printf " there, "; ecos -O; echo dear World.
⇒ Hello there, dear World.
ecos -o 1,3.orange Hello; printf " there, "; ecos -O; echo dear World.
⇒ Hello there, dear World.
#┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈
# - FILTER mode: use -f to apply colors to piped text.
# Use sed-like address and counter as 3rd and 4th args.
#
# Color all 'a' green, 2nd 'p' red, 3rd 'p' cyan, all 'e' blue
ecos -f .green a . . .red p . 2 .cyan p . 3 .blue e . . <<<"apple banana grape"
⇒ apple banana grape
# Color red the 1st character of every line ending in 7 (then fold to 1 line)
seq 5 18 | ecos -f 1/red ^. /7$/ . | xargs
⇒ 5 6 7 8 9 10 11 12 13 14 15 16 17 18
# Color 1s red, 2s green, 5s yellow, and the 4th line blue
seq 5 20 | ecos -f 1/red 1 . . i/green 2 . . 1/yellow 5 . . 1/blue . 4 . | xargs
⇒ 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
# Color 1s red, 2s green, 5s yellow, and blue the last char of lines 3, 8, 13...
seq 6 20 | ecos -f 1/red 1 . . i/green 2 . . 1/yellow 6 . . 1/blue .$ 3~5 . | xargs
⇒ 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
#┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈
# - UTILITY mode: use -u for an assortment of color utilities.
# Use these to convert between decimal triplets, hex triplets, and names
# (decimal triplets can be dot-delimited; some color names are synonymous)
ecos -u d2h 255.99.71 238.92.66
⇒
ff6347
ee5c42
ecos -u h2d ff6347 ee5c42 f00
⇒
255;99;71
238;92;66
255;0;0
ecos -u d2n 255.99.71 238.92.66
⇒
tomato1 tomato
tomato2
ecos -u h2n ff6347 ee5c42 f00
⇒
tomato1 tomato
tomato2
red1 red
ecos -u n2d tomato1 tomato2
⇒
255;99;71
238;92;66
ecos -u n2h tomato1 tomato2
⇒
ff6347
ee5c42
# Use these to see the list of color names (not shown here — try them!)
# - lnh and lnd show hex and decimal codes of colors (opt. $1: delimiter)
# ecos -u lnh ; ecos -u lnd
# ecos -u lnh s ; ecos -u lnd s
# ecos -u lnh , ; ecos -u lnd t
#
# - Use lnc to show colors in their own color (opt. rx=$1, columns=$2)
# ecos -u lnc ; ecos -u lnc blue
# ecos -u lnc . 1 ; ecos -u lnc blue 1
# ecos -u lnc . 4 ; ecos -u lnc blue 4
#
# Ok, ok. Let's show a few:
ecos -u lnc pur 5
⇒
mediumpurple1 mediumpurple2 mediumpurple3 mediumpurple4 mediumpurple
purple1 purple2 purple3 purple4 purple
ecos -u lnc en3$
⇒
darkolivegreen3
darkseagreen3
green3
palegreen3
seagreen3
springgreen3
ecos -u find 42
⇒
brown a52a2a 165 42 42
cadetblue2 8ee5ee 142 229 238
goldenrod2 eeb422 238 180 34
gray14 242424 36 36 36
gray26 424242 66 66 66
gray42 6b6b6b 107 107 107
gray95 f2f2f2 242 242 242
lightsalmon4 8b5742 139 87 66
olivedrab 6b8e23 107 142 35
sienna2 ee7942 238 121 66
tomato2 ee5c42 238 92 66
# For more: ecos -u help
#┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈
# - HELP mode: use -h to get help (not shown here).
# ecos -h
#┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
# [Developer Options]
#
# - FUNCTION HELP mode: use -hFUN to see internal ecos--FUN's docstring.
ecos -hhex2dec
⇒
ecos--hex2dec (hex1 _hex2 _hex3 …)
Convert lowercase HEX triplets to semicolon-separated decimal triplets.
#┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈ ┈
# - EXEC mode: use -=FUN to run function ecos--FUN
ecos -=hex2dec f00 c0ffee
⇒
255;0;0
192;255;238
# You most likely don't need these.
# (e.g., you'd normally get the latter with: ecos -u h2d f00 c0ffee).
Effects
[Effects]
# A few effects are usually available
ecos /4 regular. 1/4 bold. 0,2/4 dim. 0,3/4 italic.
⇒ regular.bold.dim.italic.
ecos 0,4/4 underline. 0,5/4 blink. 0,7/4 reverse. 0,8/4 hide.
⇒ underline.blink.reverse.hide.
ecos 0,9/4 strike. 0,21/4 "double underline." 0,53/4 overline.
⇒ strike.double underline.overline.
# Effects stay. That prepended "0," turns all off before passing new effect.
ecos 1/blue bold. 3 italic. 4 underline. 5 blink. 7 reverse.
⇒ bold.italic.underline.blink.reverse.
ecos 1/blue bold. 3 italic. 4/red underline. 5 blink. 7 reverse.
⇒ bold.italic.underline.blink.reverse.
ecos 1/blue bold. 3 italic. 0,4/red underline. 5 blink. 7 reverse.
⇒ bold.italic.underline.blink.reverse.
# You can turn only the color off with a "color 9". Compare with the above.
ecos 1/blue bold. 3 italic. 4/9 underline. 5 blink. 7 reverse.
⇒ bold.italic.underline.blink.reverse.
ecos 1/blue "bold blue" /9 " just bold"
⇒ bold blue just bold
# Comma-separate to turn on more than one effect at once
ecos 1,3/blue "bold and italic"
⇒ bold and italic
ecos 1,3,9/yellow_/red "Overloaded FOO."
⇒ Overloaded FOO.
# Though exemplified with 4-bit, effects also work on 8- and 24-bit colors.
# You can showcase common effects with the -e option:
ecos -e .coral1 /blue :220 .#c0ffee
⇒
0.coral1 0/blue 0:220 0.#c0ffee
1.coral1 1/blue 1:220 1.#c0ffee
2.coral1 2/blue 2:220 2.#c0ffee
3.coral1 3/blue 3:220 3.#c0ffee
4.coral1 4/blue 4:220 4.#c0ffee
5.coral1 5/blue 5:220 5.#c0ffee
7.coral1 7/blue 7:220 7.#c0ffee
9.coral1 9/blue 9:220 9.#c0ffee
53.coral1 53/blue 53:220 53.#c0ffee
21.coral1 21/blue 21:220 21.#c0ffee
# Note: dim (2) has no effect on 24-bit; and for 4-bit, bold implies intense.
4-bit
[4-bit]
# Use: <effect>/<color> — the "/" means "4-bit"
ecos 1/yellow "Hello, world! I'm bold yellow."
⇒ Hello, world! I'm bold yellow.
# A new color changes the previous
ecos 1/red "Hello" /green ", " /magenta world /cyan '!'
⇒ Hello, world!
# The 0 turns off every effect, so the " or " is in the default color and no bold
ecos 1/blue "By name" 0/cyan " or " 1/4 "by number" 0/cyan " (0–7, blue=4)"
⇒ By name or by number (0–7, blue=4)
# Bold and intense effects (intense is only for 4-bits; and no bold-regular here)
ecos 0/black regular 0 ", " i/black intense 0 ", " 1/black bold-intense
⇒ regular, intense, bold-intense
ecos 0/red regular 0 ", " i/red intense 0 ", " 1/red bold-intense
⇒ regular, intense, bold-intense
ecos 0/green regular 0 ", " i/green intense 0 ", " 1/green bold-intense
⇒ regular, intense, bold-intense
ecos 0/yellow regular 0 ", " i/yellow intense 0 ", " 1/yellow bold-intense
⇒ regular, intense, bold-intense
ecos 0/blue regular 0 ", " i/blue intense 0 ", " 1/blue bold-intense
⇒ regular, intense, bold-intense
ecos 0/magenta regular 0 ", " i/magenta intense 0 ", " 1/magenta bold-intense
⇒ regular, intense, bold-intense
ecos 0/cyan regular 0 ", " i/cyan intense 0 ", " 1/cyan bold-intense
⇒ regular, intense, bold-intense
ecos 0/white regular 0 ", " i/white intense 0 ", " 1/white bold-intense
⇒ regular, intense, bold-intense
# Blue again, but by number
ecos 0/4 regular 0 ", " i/4 intense 0 ", " 1/4 bold-intense
⇒ regular, intense, bold-intense
8-bit
[8-bit]
# Use: <effect>:<color> — the ":" means "8-bit"
# Colors are available through a single number from 0 to 255:
ecos :208 orange 0 ", " 1:208 bold-orange
⇒ orange, bold-orange
# Colors 0–7 are the same as 4-bit regular; 8–15, 4-bit intense.
# Differently from 4-bit, you can have bold without intense.
# But there's no "i" — you do it by changing the color number. Here're all 8:
ecos 0:0 regular 0 ", " 0:8 intense 0 ", " 1:0 bold 0 ", " 1:8 bold-intense
⇒ regular, intense, bold, bold-intense
ecos 0:1 regular 0 ", " 0:9 intense 0 ", " 1:1 bold 0 ", " 1:9 bold-intense
⇒ regular, intense, bold, bold-intense
ecos 0:2 regular 0 ", " 0:10 intense 0 ", " 1:2 bold 0 ", " 1:10 bold-intense
⇒ regular, intense, bold, bold-intense
ecos 0:3 regular 0 ", " 0:11 intense 0 ", " 1:3 bold 0 ", " 1:11 bold-intense
⇒ regular, intense, bold, bold-intense
ecos 0:4 regular 0 ", " 0:12 intense 0 ", " 1:4 bold 0 ", " 1:12 bold-intense
⇒ regular, intense, bold, bold-intense
ecos 0:5 regular 0 ", " 0:13 intense 0 ", " 1:5 bold 0 ", " 1:13 bold-intense
⇒ regular, intense, bold, bold-intense
ecos 0:6 regular 0 ", " 0:14 intense 0 ", " 1:6 bold 0 ", " 1:14 bold-intense
⇒ regular, intense, bold, bold-intense
ecos 0:7 regular 0 ", " 0:15 intense 0 ", " 1:7 bold 0 ", " 1:15 bold-intense
⇒ regular, intense, bold, bold-intense
# Beyond 15, you have to look them up
ecos 1:20 20, :42 42, :50 50, :90 90, :142 142, :200 200, :210 210, :215 215
⇒ 20,42,50,90,142,200,210,215
# From 232 to 255, it's all grayscale:
ecos 1:235 235_ :240 240_ :245 245_ :250 250_ :255 255_
⇒ 235_240_245_250_255_
ecos -s :2{32..55}
⇒ :232 :233 :234 :235 :236 :237 :238 :239 :240 :241 :242 :243 :244 :245 :246 :247 :248 :249 :250 :251 :252 :253 :254 :255
24-bit
[24-bit]
# Use: <effect>.<color> — the "." means "24-bit"
#
# There are different ways to express the color part. You can use:
#
# 1. Simplified hex triplets (#0ac <=> #00aacc; #3e9 <=> #33ee99)
ecos 1.#f00 f00_ .#ace ace_ .#bad bad_ .#133 133_ .#1ee 1ee
⇒ f00_ace_bad_133_1ee
# 2. Full hex triplets
ecos 1.#729fbc 729fbc_ .#bee791 bee791_ .#beefed beefed
⇒ 729fbc_bee791_beefed
ecos 1.#cafe0a cafe0a_ .#c0ffee c0ffee_ .#decaf9 decaf9
⇒ cafe0a_c0ffee_decaf9
ecos .#decade decade_ .#facade facade_ .#faded1 faded1
⇒ decade_facade_faded1
# 3. Decimal triplets
ecos 1.114.159.188 729fbc_ .190.231.145 bee791_ .190.239.237 beefed
⇒ 729fbc_bee791_beefed
ecos 1.202.254.10 cafe0a_ .192.255.238 c0ffee_ .222.202.249 decaf9
⇒ cafe0a_c0ffee_decaf9
ecos .222.202.222 decade_ .250.202.222 facade_ .250.222.209 faded1
⇒ decade_facade_faded1
# 4. X11 color names
# (-s = self-color: use the color on itself; good for examples like these)
ecos -s .{cyan,seagreen}{1,2,3,4}
⇒ .cyan1 .cyan2 .cyan3 .cyan4 .seagreen1 .seagreen2 .seagreen3 .seagreen4
ecos 1.orange Camel .cyan2 Case .seagreen2 " snake" .seagreen4 _ .cyan2 case
⇒ CamelCase snake_case
# You can mix formats — no problem.
#
# Here're some examples using both foreground and background.
# Note that the bold effect ("1") can be in either part when you have both.
ecos -s 1.tomato _1.tomato 1_.tomato .#ff0_1.tomato 1.#ff0_.tomato
⇒ 1.tomato _1.tomato 1_.tomato .#ff0_1.tomato 1.#ff0_.tomato
# In the above, the "1" is repeated because -s resets everything between
# one color and the other, so each color expression has to be complete.
# But in normal mode, previous effects/colors remain until you change them:
ecos 1.#ff0 FOO _.tomato foo .lightcyan2 FOO _.navy foo .tomato FOO _.9 foo _.#aaa FOO .9 foo _.9 FOO
⇒ FOOfooFOOfooFOOfooFOOfooFOO
# Unless you pass -r, in which case effects are reset at each step.
ecos -r 1.#ff0 FOO _.tomato foo .lightcyan2 FOO _.navy foo .tomato FOO _.9 foo _.#aaa FOO .9 foo _.9 FOO
⇒ FOOfooFOOfooFOOfooFOOfooFOO
# That "9", used in the color part, resets only the color.
# It means "default color".
# So .9 and _.9 reset foreground and background color, respectively.
#
# (There's also an effect "9": strike through, which "29" can remove.)
ecos 1,9.#ee0_.navy "Bold struck thru!" 29 " Remove strike. " 3 "Add italic. " .9 "Reset fgcolor. " _.9 "Reset bgcolor. " .tomato Tomato. 0 " Reset all."
⇒ Bold struck thru! Remove strike. Add italic. Reset fgcolor. Reset bgcolor. Tomato. Reset all.
# And here's the title and subtitle of a splendid website:
ecos .gray88_1.gray11 " Listful Andrew "; ecos .#ff4b4b_1.gray11 \` .gray55 \( .#e9b2e3 "emacs lisp bash " .#ff4b4b ,@ .#e9b2e3 functional-programming .gray55 \)
⇒
Listful Andrew
`(emacs lisp bash ,@functional-programming)
# Enjoy!
Completion
You can add completion to ecos, so that if you <tab><tab> with your cursor here:
ecos .li★
you'll see all 47 named colors that start with light, plus limegreen and linen.
This is the most useful completion, but it also works when you <tab><tab> while in other parts of an ecos command.
See ecos -h for instructions on how to enable it.
Known limitations
A couple completions don't work — but that's ok.
Completion won't work for background colors
Workaround:
- type a space after the
_and complete the color as if it were foreground - then delete that added space.
Completion won't work for 8-bit colors
That's ok:
- the syntax is trivial: any number between
0and255(e.g.,ecos :111 some-blue) - you'd still have to type these three digits anyway
Notes about these
I could have fixed both by adding this to the completion function:
COMP_WORDBREAKS="${COMP_WORDBREAKS//:}_"
Yet it's not a good idea: it'd change a global variable that could break other completions.
It's possible to work around this, but it was not worth the trouble in these cases.
Still, if you really want, you can change the variable yourself as above.
Then use ecos. Complete those cases.
Then either close that shell instance, or restore that variable with:
COMP_WORDBREAKS="${COMP_WORDBREAKS//_}:"
Installation
See my page Software for the most up-to-date instructions on how to download and install my packages.
Also run ecos -h and look into Completion and Sourcing.
Contributing
See my page Software for information about how to contribute to my packages.
License
This project follows the REUSE Specification (FAQ), which in turn is built upon SPDX.
Therefore, license and copyright information can be found in:
- each file's comment header, or
- an adjacent file of the same name with the additional extension
.license, or - the
.reuse/dep5file
The full text of the licenses can be found in the LICENSES subdirectory.
ecos
Structure
## Ecos --- Echo with millions of colors ## Commentary ### See the README for more information ## Code ### Functions #### Main #### Internal ##### Filter ##### Parse ###### Pairs ###### Colors ##### Color utilities ###### Select utility ###### Convert between color representation ####### Between hex and decimal triplets ####### To names from hex and decimal triplets ####### From names to hex and decimal triplets ###### Display color information ####### Display formatted data ####### Format raw data ####### Output raw data ##### Enable completion ##### Get help ### Source it or run it ## Ecos ends here
Contents
#!/usr/bin/env bash ## Ecos --- Echo with millions of colors # SPDX-FileCopyrightText: © flandrew <https://flandrew.srht.site/listful> # SPDX-License-Identifier: GPL-3.0-or-later #---------------------# # Author: flandrew # # Created: 2025-12-22 # # Updated: 2026-01-07 # #---------------------# # Version: 0.2.0 # #---------------------# ## Commentary # # Ecos makes it easy to add colors and effects to text in your terminal. # # To know how to use it, run: ecos -h (or -H) # ### See the README for more information # # A local README.org should be available in the same directory as this file. # # You can also read it online: # https://flandrew.srht.site/listful/sw-bash-ecos.html # ############################################################################# #⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ## Code ### Functions #### Main ecos() { : _options <<_ Ecos --- Echo with millions of colors Usage: ecos [-n] [-r] COLOR1 STRING1 [COLOR2 STRING2 …] ecos -s COLOR1 [COLOR2 …] ecos -e COLOR1 [COLOR2 …] ecos -f COLOR1 RX1 ADDRESS1 COUNT1 [COLOR2 RX2 ADDRESS2 COUNT2 …] < <(foo) ecos -o COLOR1 [STRING1] ecos -O ecos -u [UTIL-OPTIONS] ecos --complete ecos -(h|H) Options: | Flag | Description | |------+------------------------------------------------------------------| | | regular mode: interleave colors and strings | | -n | - and suppress trailing newline | | -r | - and reset colors and effects at each step (no inheritance) | |------+------------------------------------------------------------------| | -s | self mode: colorize colors with themselves | | -e | effect mode: showcase effects of colors | | -f | filter mode: colorize stdin using sed-like addresses and counts | | -o | open mode: start colors that next commands can inherit | | -O | close mode: reset any open colors | |------+------------------------------------------------------------------| | -u | utility mode: convert, display, and search colors | |------+------------------------------------------------------------------| | -h | help (all this, with some colors) | | -H | help (all this, monochrome) | _____________________________________________________________________________ Regular: ecos [-n] [-r] COLOR1 STRING1 [COLOR2 STRING2 …] Complete colors look like this: $effect$depth$color_$effect$depth$color - That's $foreground_$background. Either can be omitted. - An $effect is zero or more comma-separated numbers. There're many effects; the most common are 0 (reset) and 1 (bold). - $depth is one of [/:.] for 4, 8, 24-bits respectively. - $color format depends on the depth. - 4-bit colors: black, red, green, blue, yellow, cyan, magenta, white - 8-bit colors: a number from 0 to 255 - 24-bit colors: three input options: - X11 color name: tomato1 - Hex triplet: #ff6347 (short hex is ok, too: #ace == #aaccee) - Dec triplet: 255.99.71 So this: ecos 1,3.sandybrown_.purple3 "Hello, world!" outputs that string in bold+italic sandybrown on a purple3 background. _____________________________________________________________________________ Self: ecos -s COLOR1 [COLOR2 …] Just try: ecos -s 1/red 0/blue 1:208 3.#f00 .#c9fe02 1.#ff40a7 .200.27.140 1.orange1_.navy _____________________________________________________________________________ Effect: ecos -e COLOR1 [COLOR2 …] Just try: ecos -e .skyblue1 /green :200 _____________________________________________________________________________ Filter: ecos -f COLOR1 RX1 ADDRESS1 COUNT1 [COLOR2 RX2 ADDRESS2 COUNT2 …] < <(foo) where address: 3 1-5 3,7 2~2 /^# / etc. (Use '.' to replace on every line) count: number of replacements (Use '.' to replace all in the line) It's a simple filter to add colors to text. If you're familiar with sed syntax, this is what the filter does: ecos -f /red foob.. 2-3 4 <=> sed -E "2-3 s/foob../${RED}&${NONE}/4" where RED and NONE are color escape sequences wrappers around "foob.." So this: - colorizes the 4th match, - of the regular expression "foob.." (which will match, e.g., "foobar"), - on lines 2 and 3, - of input coming from stdin. Just try: ecos -f .green a . . .red p . 2 .cyan p . 3 .blue e . . <<<"apple banana grape" ecos -f 1/red 1 . . i/green 2 . . 1/yellow 5 . . 1/blue . 4 . < <(seq 5 20) ecos -f 1/red ^. /7$/ . < <(seq 5 18) _____________________________________________________________________________ Open: ecos -o COLOR1 [STRING1] Close: ecos -O Just try: ecos -o 1,3.orange Hello printf " there, " ecos -O echo dear World. _____________________________________________________________________________ Utilities: ecos -u [UTIL-OPTIONS] To read its own help, simply: ecos -u _____________________________________________________________________________ Completion: To enable it, add this line to your ~/.bash_aliases or ~/.bashrc: . <(ecos --complete) or, if ecos executable is not in your PATH and hasn't been sourced: . <(/path/to/ecos --complete) and then, when writing an ecos command, press <tab><tab> to show completions. Note that your interactive shell must be Bash for completions to work. _____________________________________________________________________________ Sourcing: If you intend to use ecos in a Bash file that'll call it repeatedly, you may want to source the file instead of executing it. There's usually a gain in speed. For example, the examples-ecos file seems to run about 30% faster now that it's sourcing ecos. Add something like this to the file that'll source it: . /path/to/ecos || . ecos It'll look for the file ecos in dir /path/to/, and if it doesn't find it (maybe you end up moving it?) it'll try to find ecos somewhere in your PATH. Or you could put just the second part, if you're sure it'll be in your PATH. _____________________________________________________________________________ Read: https://en.wikipedia.org/wiki/X11_color_names https://en.wikipedia.org/wiki/ANSI_escape_code See sections "SGR (Select Graphic Rendition) parameters" and "Colors". man 4 console_codes See section "ECMA-48 Select Graphic Rendition". _____________________________________________________________________________ _ LC_ALL=C.utf8 [[ "$1" ]] || return 0 hash getopt grep sed xargs || return 127 local mod nnl rst # mode nonewline reset opt=$(getopt -n ecos -l 'complete' -o 'nrfoOseuh::H::=:' -- "$@") eval set -- "$opt" while :; do case "$1" in -n) mod=norm nnl=true ;; -r) mod=norm rst=true ;; -f) mod=filt ;; -o) mod=open ;; -O) mod=clos ;; -s) mod=self ;; -e) mod=effc ;; -u) mod=util; shift 2 ; break ;; -h) ecos--doc "$2" ; return "$?" ;; -H) ecos--doc-nc "$2" ; return "$?" ;; -=) ecos--"$2" "${@:4}" ; return "$?" ;; --complete) ecos--complete ; return "$?" ;; --) shift ; break ;; * ) echo >&2 "Error" ; return 1 ;; esac; shift ; continue done case "${mod:=norm}" in norm) if "${rst:=false}" then ecos--parse-pairs-with-reset "$@" else ecos--parse-pairs "$@" fi; "${nnl:=false}" || echo ;; filt) ecos--filter "$@" ;; effc) ecos--parse-effect "$@" ;; self) ecos--parse-self "$@" ;; open) ecos--parse-pair "$@" ;; # "$2" is optional clos) ecos--parse-pair 0 "" ;; util) ecos--utils "$@" ;; esac ;} #⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ #### Internal ##### Filter ecos--filter() { : cclr1 rx1 address1 count1 _cclr2 _rx2 _address2 _count2 … <<_ A simple filter to add colors to text. See ecos -h for more information. _ local sc="" co rx cn ad # sed command, color, regex, count, address while (("$#">0)); do co="$(ecos--parse-completecolor "$1")" rx="$2" if [[ "$3" == "." ]]; then ad=""; else ad="$3"; fi if [[ "$4" == "." ]]; then cn=g; else cn="$4"; fi if [[ "$co" ]]; then sc+="$ad s/$rx/$co&\e[0m/$cn;"; fi shift 4 done;: "${sc@E}"; sed -E "${_:-N}" ;} #⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ##### Parse ###### Pairs ecos--parse-effect() { : color1 _color2 _color3 … <<_ Show application of common effects to user-provided colors, space-separated. _ shopt -s extglob; local ef co for ef in 0 1 2 3 4 5 7 9 53 21 do for co; do ecos--parse-self "$ef${co/#+([0-9,])/}"; done done | xargs -n"$#" | if hash column 2>/dev/null; then column -t; else sed 's/ / /g'; fi shopt -u extglob ;} ecos--parse-self() { : completecolor1 _completecolor2 _completecolor3 … <<_ Colorize user-provided colors with themselves, space-separated. Color is reset before each and at its end. _ ecos--reset while (("$#")); do ecos--parse-pair "$1" "$1" ecos--reset " "; shift; done | sed '$ s/ $/\n/' ;} ecos--parse-pairs-with-reset() { : completecolor1 string1 _completecolor2 _string2 … <<_ Parse user-provided pairs with ecos--parse-pair, which see. Color is reset before each and at its end. Return colorized string. _ while (("$#">=2)); do ecos--reset ecos--parse-pair "$1" "$2"; shift 2; done; ecos--reset ;} ecos--parse-pairs() { : completecolor1 string1 _completecolor2 _string2 … <<_ Parse user-provided pairs with ecos--parse-pair, which see. Color is reset at its end. Return colorized string. _ while (("$#">=2)); do ecos--parse-pair "$1" "$2"; shift 2; done; ecos--reset ;} ecos--parse-pair() { : completecolor string <<_ Parse user-provided pair of COMPLETECOLOR plus STRING. Return colorized string. Color is not reset at its end. _ printf -- '%s%s' "$(ecos--parse-completecolor "$1")" "${2@E}" ;} ecos--reset() { : _string <<_ Insert sequence to reset effects. Optionally add STRING. Previous effects will then no longer be inherited by next items. _ printf -- "\e[0m%s" "$1" ;} #⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ###### Colors ecos--parse-completecolor() { : completecolor <<_ Parse user-provided COMPLETECOLOR. COMPLETECOLOR includes foreground and/or background, each of which can include optional effect and/or intensity. Return escape sequence. _ local rx="([,0-9]*)(i*)([:/.]?)([^_]*)" {e,i,d,c,p}{f,b} if [[ "$1" =~ ^($rx)?(_$rx)?$ ]]; then ef="${BASH_REMATCH[2]//,/;}" # [e]ffect [f]oreground if="${BASH_REMATCH[3]}" # [i]ntensity df="${BASH_REMATCH[4]}" # [d]epth cf="${BASH_REMATCH[5]}" # [c]olor eb="${BASH_REMATCH[7]//,/;}" # [e]ffect [b]ackground ib="${BASH_REMATCH[8]}" # [i]ntensity db="${BASH_REMATCH[9]}" # [d]epth cb="${BASH_REMATCH[10]}" # [c]olor case "$df" in [.]): 24;; /): 4;; :): 8;; esac; df="$_" case "$db" in [.]): 24;; /): 4;; :): 8;; esac; db="$_" [[ -z "$cf" ]] && pf="" || pf=$(ecos--parse-"$df" "$cf") [[ -z "$cb" ]] && pb="" || pb=$(ecos--parse-"$db" "$cb") [[ -z "$if" ]] && if=0 || if=6 # 3x→ 9x (e.g. 31m→ 91m) [[ -z "$ib" ]] && ib=0 || ib=6 # 4x→10x (e.g. 42m→102m) [[ "${ef%;}" ]] && ef+=";" ||: [[ "${eb%;}" ]] && eb+=";" ||: ((df==4)) && : "$if" || : 0; [[ "$pf" ]] && pf="$((3+_))$pf"||: ((db==4)) && : "$ib" || : 0; [[ "$pb" ]] && pb="$((4+_))$pb"||: : "$ef$pf$eb$pb"; else : 39; fi; printf -- '\e[%sm' "${_%;}" ;} ecos--parse-() { :;} # Ignore color if no depth marker is passed. ecos--parse-4() { : color <<_ Parse user-provided stripped COLOR, type 4 bits. _ case "$1" in [0-9]): "$1";; black): 0;; red ): 1;; green): 2;; yellow): 3;; blue ): 4;; magenta): 5;; cyan ): 6;; white ): 7;; *): 9;; esac; printf "%s;" "$_" ;} ecos--parse-8() { : color <<_ Parse user-provided stripped COLOR, type 8 bits. _ if [[ "$1" =~ ^[0-9]+$ ]] then printf "8;5;%s;" "$1" else printf "9;" fi ;} ecos--parse-24() { : color <<_ Parse user-provided stripped COLOR, type 24 bits. _ if [[ "$1" =~ ^[0-9A-Za-z]+$ ]] then ecos--nam2dec "${BASH_REMATCH[0],,}" elif [[ "$1" =~ ^#([0-9A-Fa-f]+)$ ]] then ecos--hex2dec "${BASH_REMATCH[1],,}" elif [[ "$1" =~ ^([0-9]{1,3})[.:\;]([0-9]{1,3})[.:\;]([0-9]{1,3})$ ]] then printf "%s;%s;%s" "${BASH_REMATCH[@]:1:3}" else true fi | if res="$(</dev/stdin)" grep -q ';' <<< "$res" then printf "8;2;%s;" "$res" else printf "9;" fi ;} #⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ##### Color utilities ###### Select utility ecos--utils() { : fun args <<_ Convert, list, display, or search colors. Usage: ecos -u FUN ARGS Convert: FUN can be one of these conversion functions: - h2d = from hex to dec - d2h = from dec to hex - n2d = from nam to dec - n2h = from nam to hex - d2n = from dec to nam - h2n = from hex to nam whose ARGS are, depending on FUN, one or more colors as: - hexadecimal (ff0000 or f00) - decimal (255.0.0) - name (red) List: FUN can also be one of these name-listing functions: - lnd = color names as decimals - lnh = color names as hex - lnhd = color names as decimals and hex In this case, ARG is an optional field delimiter. If omitted, colon is used. Otherwise: - t makes it tab-separated - s makes it space-separated (columns) - any other character (e.g., a comma) is used literally as delimiter. Display: FUN can also be this name-display function: - lnc = color names in their colors Optional 1st ARG is a regular expression: filter what colors to show. Optional 2nd ARG is an integer: show only names, in that many columns. Search: FUN can also be this search function: - find = show all info available on named colors that match this regex Here, ARG is a regular expression. It can be part of a color name, an hex, or a decimal. Try: ecos -u find ff1 # will match hex and a name ecos -u find 42 # will match dec, hex, and a name Pageless: When FUN is ln* or find, the results are paged with less. To remove paging, just pipe it to cat: ecos -u lnc o.2 3 | cat _ case "$1" in (d2h) ecos--dec2hex "${@:2}" ;; (h2d) ecos--hex2dec "${@:2}" ;; (d2n) ecos--dec2nam "${@:2}" ;; (h2n) ecos--hex2nam "${@:2}" ;; (n2d) ecos--nam2dec "${@:2}" ;; (n2h) ecos--nam2hex "${@:2}" ;; (lnc) ecos--nam-col "${@:2}" ;; (lnd) ecos--nam-dec "${@:2}" ;; (lnh) ecos--nam-hex "${@:2}" ;; (lnhd) ecos--nam-h-d "${@:2}" ;; (find) ecos--find-rx "${@:2}" ;; (* ) ecos--doc utils ;; esac ;} #⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ###### Convert between color representation ####### Between hex and decimal triplets ecos--dec2hex() { : dec1 _dec2 _dec3 … <<_ Convert decimal triplets to lowercase hex triplets. The triplet(s) can be separated by dots, colons, or semicolons. _ for color; do if [[ "$color" =~ ^([0-9]{1,3})[.:\;]([0-9]{1,3})[.:\;]([0-9]{1,3})$ ]] then declare r g b read -r _ r g b <<< "${BASH_REMATCH[*]}" printf "%02x%02x%02x\n" "$r" "$g" "$b" else printf "\n" fi done ;} ecos--hex2dec() { : hex1 _hex2 _hex3 … <<_ Convert lowercase HEX triplets to semicolon-separated decimal triplets. _ for color; do if [[ "$color" =~ ^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$ ]] then declare r g b read -r _ r g b <<< "${BASH_REMATCH[*]}" ((r=0x"$r" , g=0x"$g" , b=0x"$b" , 1)) printf "%s;%s;%s\n" "$r" "$g" "$b" elif [[ "$color" =~ ^([0-9a-f]{1})([0-9a-f]{1})([0-9a-f]{1})$ ]] then declare r g b read -r _ r g b <<< "${BASH_REMATCH[*]}" ((r=0x"$r$r", g=0x"$g$g", b=0x"$b$b", 1)) printf "%s;%s;%s\n" "$r" "$g" "$b" else printf "\n" fi done ;} #⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ####### To names from hex and decimal triplets ecos--dec2nam() { : dec1 _dec2 _dec3 <<_ Convert decimal triplets to color names. The triplets can be separated by dots, colons, or semicolons. _ for color; do ecos--nam-dec-raw | grep -P ":${color//[.;]/:}$" | cut -f1 -d: | xargs done ;} ecos--hex2nam() { : hex1 _hex2 _hex3 … <<_ Convert lowercased HEX triplets to color names. _ for color; do : "$(ecos--hex2dec "$color")" if [[ "$_" ]]; then ecos--dec2nam "$_"; fi done ;} #⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ####### From names to hex and decimal triplets ecos--nam2dec() { : colorname1 _colorname2 _colorname3 … <<_ Convert COLORNAMEs to semicolon-separated decimal triplets. _ for color; do ecos--nam-dec-raw | grep -P "^${color//grey/gray}:" | cut -f2- -d: | tr : \; done ;} ecos--nam2hex() { : colorname1 _colorname2 _colorname3 … <<_ Convert COLORNAMEs to lowercased hex triplets. _ for color; do : "$(ecos--nam2dec "$color")" if [[ "$_" ]]; then ecos--dec2hex "$_"; fi done ;} #⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ###### Display color information ####### Display formatted data ecos--nam-col() { : _regex _ncols <<_ Output color names in their own colors beside a background of the same. Optional first argument is a regular expression: filter what colors to show. Optional second argument is an integer: show only names, in that many columns. _ local rx="${1:-.}" n="$2" ecos--nam-dec-raw | if [[ "$rx" == "." ]] then cat else cut -f1 -d: | grep -P "$rx" | (: "^($(tr '\n' '|')):" ecos--nam-dec-raw | grep -P "$_") fi | ecos--colorize-colors "$n" | if [[ "$n" =~ ^[0-9]+$ ]] && (("$n">1)) then xargs -n"$n" | if hash column 2>/dev/null then column -t; else sed "s/ / /g"; fi else cat fi | \less -R ;} ecos--nam-dec() { : _delimiter <<_ Output data with four fields: COLORNAME R G B COLORNAME is lowercased, no spaces. R, G, B are decimals in 0–255 range. DELIMITER, if omitted, is colon. Otherwise: - t makes it tab-separated - s makes it space-separated (columns) - any other character (e.g., a comma) is used literally as delimiter. _ ecos--nam-dec-raw | ecos--change-delimiter "$1" | \less ;} ecos--nam-hex() { : _delimiter <<_ Output data with two fields: COLORNAME HEXTRIPLET COLORNAME is lowercased, no spaces. HEXTRIPLET is lowercased in 00–ff tange. For the meaning of DELIMITER, see ecos--nam-dec. _ paste -d: \ <(ecos--nam-dec-raw | cut -d: -f1) \ <(mapfile -t < <(ecos--nam-dec-raw | cut -d: -f2-) ecos--dec2hex "${MAPFILE[@]}") | ecos--change-delimiter "$1" | \less ;} ecos--nam-h-d() { : _delimiter <<_ Output data with five fields: COLORNAME HEXTRIPLET R G B For the meaning of these variables, see ecos--nam-dec and ecos--nam-hex. _ join <(ecos--nam-hex) \ <(ecos--nam-dec) -t: | ecos--change-delimiter "$1" | \less ;} ecos--find-rx() { : regex <<_ Show all info available on colors that match REGEX. REGEX can be part of a color name, an hex, or a decimal. _ local rx="$1" paste <(ecos--nam-h-d ';' | grep -P --color=none "$rx" | while IFS=$';' read -r _ _ rgb do : "\e[48;2;${rgb}m \e[0m" printf -- '%s\n' "${_@E}" done) <(ecos--nam-h-d | grep -P --color=none "$rx" | ecos--change-delimiter s | grep -P --color=always "$rx") | sed 's/\t/ /' | \less -x4 -R ;} #⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ####### Format raw data ecos--colorize-colors() { : _nobg <<_ Filter to apply decimal RGB to color names beside a background of the same. Passing anything as first argument shows only the names (no background). _ [[ "$1" ]] && : "" || : "\x1b[48:2:\2m \x1b[0m " sed -E "s/^([^:]+):(.*)/$_\x1b[38:2:\2m\1\x1b[0m/ ; s/:/;/g" ;} ecos--change-delimiter() { : _new-delimiter _old-delimiter <<_ Filter to change field delimiters. _ : "${2:-:}" # three colons, three different meanings... :) case "$1" in "") cat ;; s ) column -ts"$_" ;; t ) tr "$_" '\t' ;; * ) tr "$_" "${1:0:1}" ;; esac ;} #⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ####### Output raw data ecos--nam-dec-raw() { : <<_ Output colon-delimited data with four fields: COLORNAME R G B COLORNAME is lowercased, no spaces. R, G, B are decimals in 0–255 range. _ tr , "\n" <<<"\ aliceblue:240:248:255,antiquewhite1:255:239:219,antiquewhite2:238:223:204 antiquewhite3:205:192:176,antiquewhite4:139:131:120,antiquewhite:250:235:215 aquamarine1:127:255:212,aquamarine2:118:238:198,aquamarine3:102:205:170 aquamarine4:69:139:116,aquamarine:127:255:212,azure1:240:255:255 azure2:224:238:238,azure3:193:205:205,azure4:131:139:139,azure:240:255:255 beige:245:245:220,bisque1:255:228:196,bisque2:238:213:183,bisque3:205:183:158 bisque4:139:125:107,bisque:255:228:196,black:0:0:0,blanchedalmond:255:235:205 blue1:0:0:255,blue2:0:0:238,blue3:0:0:205,blue4:0:0:139,blueviolet:138:43:226 blue:0:0:255,brown1:255:64:64,brown2:238:59:59,brown3:205:51:51,brown4:139:35:35 brown:165:42:42,burlywood1:255:211:155,burlywood2:238:197:145 burlywood3:205:170:125,burlywood4:139:115:85,burlywood:222:184:135 cadetblue1:152:245:255,cadetblue2:142:229:238,cadetblue3:122:197:205 cadetblue4:83:134:139,cadetblue:95:158:160,chartreuse1:127:255:0 chartreuse2:118:238:0,chartreuse3:102:205:0,chartreuse4:69:139:0 chartreuse:127:255:0,chocolate1:255:127:36,chocolate2:238:118:33 chocolate3:205:102:29,chocolate4:139:69:19,chocolate:210:105:30 coral1:255:114:86,coral2:238:106:80,coral3:205:91:69,coral4:139:62:47 coral:255:127:80,cornflowerblue:100:149:237,cornsilk1:255:248:220 cornsilk2:238:232:205,cornsilk3:205:200:177,cornsilk4:139:136:120 cornsilk:255:248:220,cyan1:0:255:255,cyan2:0:238:238,cyan3:0:205:205 cyan4:0:139:139,cyan:0:255:255,darkblue:0:0:139,darkcyan:0:139:139 darkgoldenrod1:255:185:15,darkgoldenrod2:238:173:14,darkgoldenrod3:205:149:12 darkgoldenrod4:139:101:8,darkgoldenrod:184:134:11,darkgray:169:169:169 darkgreen:0:100:0,darkkhaki:189:183:107,darkmagenta:139:0:139 darkolivegreen1:202:255:112,darkolivegreen2:188:238:104 darkolivegreen3:162:205:90,darkolivegreen4:110:139:61,darkolivegreen:85:107:47 darkorange1:255:127:0,darkorange2:238:118:0,darkorange3:205:102:0 darkorange4:139:69:0,darkorange:255:140:0,darkorchid1:191:62:255 darkorchid2:178:58:238,darkorchid3:154:50:205,darkorchid4:104:34:139 darkorchid:153:50:204,darkred:139:0:0,darksalmon:233:150:122 darkseagreen1:193:255:193,darkseagreen2:180:238:180,darkseagreen3:155:205:155 darkseagreen4:105:139:105,darkseagreen:143:188:143,darkslateblue:72:61:139 darkslategray1:151:255:255,darkslategray2:141:238:238,darkslategray3:121:205:205 darkslategray4:82:139:139,darkslategray:47:79:79,darkturquoise:0:206:209 darkviolet:148:0:211,deeppink1:255:20:147,deeppink2:238:18:137 deeppink3:205:16:118,deeppink4:139:10:80,deeppink:255:20:147 deepskyblue1:0:191:255,deepskyblue2:0:178:238,deepskyblue3:0:154:205 deepskyblue4:0:104:139,deepskyblue:0:191:255,dimgray:105:105:105 dodgerblue1:30:144:255,dodgerblue2:28:134:238,dodgerblue3:24:116:205 dodgerblue4:16:78:139,dodgerblue:30:144:255,firebrick1:255:48:48 firebrick2:238:44:44,firebrick3:205:38:38,firebrick4:139:26:26 firebrick:178:34:34,floralwhite:255:250:240,forestgreen:34:139:34 gainsboro:220:220:220,ghostwhite:248:248:255,gold1:255:215:0,gold2:238:201:0 gold3:205:173:0,gold4:139:117:0,goldenrod1:255:193:37,goldenrod2:238:180:34 goldenrod3:205:155:29,goldenrod4:139:105:20,goldenrod:218:165:32,gold:255:215:0 gray0:0:0:0,gray1:3:3:3,gray2:5:5:5,gray3:8:8:8,gray4:10:10:10,gray5:13:13:13 gray6:15:15:15,gray7:18:18:18,gray8:20:20:20,gray9:23:23:23,gray10:26:26:26 gray11:28:28:28,gray12:31:31:31,gray13:33:33:33,gray14:36:36:36,gray15:38:38:38 gray16:41:41:41,gray17:43:43:43,gray18:46:46:46,gray19:48:48:48,gray20:51:51:51 gray21:54:54:54,gray22:56:56:56,gray23:59:59:59,gray24:61:61:61,gray25:64:64:64 gray26:66:66:66,gray27:69:69:69,gray28:71:71:71,gray29:74:74:74,gray30:77:77:77 gray31:79:79:79,gray32:82:82:82,gray33:84:84:84,gray34:87:87:87,gray35:89:89:89 gray36:92:92:92,gray37:94:94:94,gray38:97:97:97,gray39:99:99:99 gray40:102:102:102,gray41:105:105:105,gray42:107:107:107,gray43:110:110:110 gray44:112:112:112,gray45:115:115:115,gray46:117:117:117,gray47:120:120:120 gray48:122:122:122,gray49:125:125:125,gray50:127:127:127,gray51:130:130:130 gray52:133:133:133,gray53:135:135:135,gray54:138:138:138,gray55:140:140:140 gray56:143:143:143,gray57:145:145:145,gray58:148:148:148,gray59:150:150:150 gray60:153:153:153,gray61:156:156:156,gray62:158:158:158,gray63:161:161:161 gray64:163:163:163,gray65:166:166:166,gray66:168:168:168,gray67:171:171:171 gray68:173:173:173,gray69:176:176:176,gray70:179:179:179,gray71:181:181:181 gray72:184:184:184,gray73:186:186:186,gray74:189:189:189,gray75:191:191:191 gray76:194:194:194,gray77:196:196:196,gray78:199:199:199,gray79:201:201:201 gray80:204:204:204,gray81:207:207:207,gray82:209:209:209,gray83:212:212:212 gray84:214:214:214,gray85:217:217:217,gray86:219:219:219,gray87:222:222:222 gray88:224:224:224,gray89:227:227:227,gray90:229:229:229,gray91:232:232:232 gray92:235:235:235,gray93:237:237:237,gray94:240:240:240,gray95:242:242:242 gray96:245:245:245,gray97:247:247:247,gray98:250:250:250,gray99:252:252:252 gray100:255:255:255,gray:190:190:190,green1:0:255:0,green2:0:238:0 green3:0:205:0,green4:0:139:0,greenyellow:173:255:47,green:0:255:0 honeydew1:240:255:240,honeydew2:224:238:224,honeydew3:193:205:193 honeydew4:131:139:131,honeydew:240:255:240,hotpink1:255:110:180 hotpink2:238:106:167,hotpink3:205:96:144,hotpink4:139:58:98,hotpink:255:105:180 indianred1:255:106:106,indianred2:238:99:99,indianred3:205:85:85 indianred4:139:58:58,indianred:205:92:92,ivory1:255:255:240,ivory2:238:238:224 ivory3:205:205:193,ivory4:139:139:131,ivory:255:255:240,khaki1:255:246:143 khaki2:238:230:133,khaki3:205:198:115,khaki4:139:134:78,khaki:240:230:140 lavenderblush1:255:240:245,lavenderblush2:238:224:229,lavenderblush3:205:193:197 lavenderblush4:139:131:134,lavenderblush:255:240:245,lavender:230:230:250 lawngreen:124:252:0,lemonchiffon1:255:250:205,lemonchiffon2:238:233:191 lemonchiffon3:205:201:165,lemonchiffon4:139:137:112,lemonchiffon:255:250:205 lightblue1:191:239:255,lightblue2:178:223:238,lightblue3:154:192:205 lightblue4:104:131:139,lightblue:173:216:230,lightcoral:240:128:128 lightcyan1:224:255:255,lightcyan2:209:238:238,lightcyan3:180:205:205 lightcyan4:122:139:139,lightcyan:224:255:255,lightgoldenrod1:255:236:139 lightgoldenrod2:238:220:130,lightgoldenrod3:205:190:112 lightgoldenrod4:139:129:76,lightgoldenrodyellow:250:250:210 lightgoldenrod:238:221:130,lightgray:211:211:211,lightgreen:144:238:144 lightpink1:255:174:185,lightpink2:238:162:173,lightpink3:205:140:149 lightpink4:139:95:101,lightpink:255:182:193,lightsalmon1:255:160:122 lightsalmon2:238:149:114,lightsalmon3:205:129:98,lightsalmon4:139:87:66 lightsalmon:255:160:122,lightseagreen:32:178:170,lightskyblue1:176:226:255 lightskyblue2:164:211:238,lightskyblue3:141:182:205,lightskyblue4:96:123:139 lightskyblue:135:206:250,lightslateblue:132:112:255,lightslategray:119:136:153 lightsteelblue1:202:225:255,lightsteelblue2:188:210:238 lightsteelblue3:162:181:205,lightsteelblue4:110:123:139 lightsteelblue:176:196:222,lightyellow1:255:255:224,lightyellow2:238:238:209 lightyellow3:205:205:180,lightyellow4:139:139:122,lightyellow:255:255:224 limegreen:50:205:50,linen:250:240:230,magenta1:255:0:255,magenta2:238:0:238 magenta3:205:0:205,magenta4:139:0:139,magenta:255:0:255,maroon1:255:52:179 maroon2:238:48:167,maroon3:205:41:144,maroon4:139:28:98,maroon:176:48:96 mediumaquamarine:102:205:170,mediumblue:0:0:205,mediumorchid1:224:102:255 mediumorchid2:209:95:238,mediumorchid3:180:82:205,mediumorchid4:122:55:139 mediumorchid:186:85:211,mediumpurple1:171:130:255,mediumpurple2:159:121:238 mediumpurple3:137:104:205,mediumpurple4:93:71:139,mediumpurple:147:112:219 mediumseagreen:60:179:113,mediumslateblue:123:104:238 mediumspringgreen:0:250:154,mediumturquoise:72:209:204 mediumvioletred:199:21:133,midnightblue:25:25:112,mintcream:245:255:250 mistyrose1:255:228:225,mistyrose2:238:213:210,mistyrose3:205:183:181 mistyrose4:139:125:123,mistyrose:255:228:225,moccasin:255:228:181 navajowhite1:255:222:173,navajowhite2:238:207:161,navajowhite3:205:179:139 navajowhite4:139:121:94,navajowhite:255:222:173,navy:0:0:128,oldlace:253:245:230 olivedrab1:192:255:62,olivedrab2:179:238:58,olivedrab3:154:205:50 olivedrab4:105:139:34,olivedrab:107:142:35,orange1:255:165:0,orange2:238:154:0 orange3:205:133:0,orange4:139:90:0,orangered1:255:69:0,orangered2:238:64:0 orangered3:205:55:0,orangered4:139:37:0,orangered:255:69:0,orange:255:165:0 orchid1:255:131:250,orchid2:238:122:233,orchid3:205:105:201,orchid4:139:71:137 orchid:218:112:214,palegoldenrod:238:232:170,palegreen1:154:255:154 palegreen2:144:238:144,palegreen3:124:205:124,palegreen4:84:139:84 palegreen:152:251:152,paleturquoise1:187:255:255,paleturquoise2:174:238:238 paleturquoise3:150:205:205,paleturquoise4:102:139:139,paleturquoise:175:238:238 palevioletred1:255:130:171,palevioletred2:238:121:159,palevioletred3:205:104:137 palevioletred4:139:71:93,palevioletred:219:112:147,papayawhip:255:239:213 peachpuff1:255:218:185,peachpuff2:238:203:173,peachpuff3:205:175:149 peachpuff4:139:119:101,peachpuff:255:218:185,peru:205:133:63,pink1:255:181:197 pink2:238:169:184,pink3:205:145:158,pink4:139:99:108,pink:255:192:203 plum1:255:187:255,plum2:238:174:238,plum3:205:150:205,plum4:139:102:139 plum:221:160:221,powderblue:176:224:230,purple1:155:48:255,purple2:145:44:238 purple3:125:38:205,purple4:85:26:139,purple:160:32:240,red1:255:0:0,red2:238:0:0 red3:205:0:0,red4:139:0:0,red:255:0:0,rosybrown1:255:193:193 rosybrown2:238:180:180,rosybrown3:205:155:155,rosybrown4:139:105:105 rosybrown:188:143:143,royalblue1:72:118:255,royalblue2:67:110:238 royalblue3:58:95:205,royalblue4:39:64:139,royalblue:65:105:225 saddlebrown:139:69:19,salmon1:255:140:105,salmon2:238:130:98,salmon3:205:112:84 salmon4:139:76:57,salmon:250:128:114,sandybrown:244:164:96,seagreen1:84:255:159 seagreen2:78:238:148,seagreen3:67:205:128,seagreen4:46:139:87,seagreen:46:139:87 seashell1:255:245:238,seashell2:238:229:222,seashell3:205:197:191 seashell4:139:134:130,seashell:255:245:238,sienna1:255:130:71,sienna2:238:121:66 sienna3:205:104:57,sienna4:139:71:38,sienna:160:82:45,skyblue1:135:206:255 skyblue2:126:192:238,skyblue3:108:166:205,skyblue4:74:112:139 skyblue:135:206:235,slateblue1:131:111:255,slateblue2:122:103:238 slateblue3:105:89:205,slateblue4:71:60:139,slateblue:106:90:205 slategray1:198:226:255,slategray2:185:211:238,slategray3:159:182:205 slategray4:108:123:139,slategray:112:128:144,snow1:255:250:250,snow2:238:233:233 snow3:205:201:201,snow4:139:137:137,snow:255:250:250,springgreen1:0:255:127 springgreen2:0:238:118,springgreen3:0:205:102,springgreen4:0:139:69 springgreen:0:255:127,steelblue1:99:184:255,steelblue2:92:172:238 steelblue3:79:148:205,steelblue4:54:100:139,steelblue:70:130:180,tan1:255:165:79 tan2:238:154:73,tan3:205:133:63,tan4:139:90:43,tan:210:180:140 thistle1:255:225:255,thistle2:238:210:238,thistle3:205:181:205 thistle4:139:123:139,thistle:216:191:216,tomato1:255:99:71,tomato2:238:92:66 tomato3:205:79:57,tomato4:139:54:38,tomato:255:99:71,turquoise1:0:245:255 turquoise2:0:229:238,turquoise3:0:197:205,turquoise4:0:134:139 turquoise:64:224:208,violetred1:255:62:150,violetred2:238:58:140 violetred3:205:50:120,violetred4:139:34:82,violetred:208:32:144 violet:238:130:238,wheat1:255:231:186,wheat2:238:216:174,wheat3:205:186:150 wheat4:139:126:102,wheat:245:222:179,whitesmoke:245:245:245,white:255:255:255 yellow1:255:255:0,yellow2:238:238:0,yellow3:205:205:0,yellow4:139:139:0 yellowgreen:154:205:50,yellow:255:255:0" ;} #⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ##### Enable completion ecos--complete() { : <<_ Output commands necessary to enable completion. _ type _ecos | sed 1d echo "complete -o bashdefault -F _ecos ecos" ;} _ecos() { : <<_ Enable completion for ecos. Add this line to your ~/.bash_aliases or ~/.bashrc: . <(ecos --complete) or, if ecos executable is not in your PATH and hasn't been sourced: . <(/path/to/ecos --complete) and then, when writing an ecos command, press <tab><tab> to show completions. _ COMPREPLY=() local pro="$1" cur="$2" pre="$3" W="" \ eff="0 1 2 3 4 5 7 8 9 21 53" \ ops="-O -f -h -H -n -r -o -s -e -u" \ dep="/ . :" case "$pre" in -h|-H) : "" ;; -u) : "help d2h h2d d2n h2n n2d n2h lnc lnd lnh lnhd find" ;; n2[dh]) : "$(ecos -=nam-dec-raw | cut -f1 -d:)" ;; find) : "REGEX" ;; lnc) : "NOTHING REGEX" ;; ln*) : "NOTHING t s ," ;; "$pro") : "$eff i $dep -" ;; -s) : "$eff i $dep" ;; -e) : "i $dep" ;; *) if [[ "$pre" =~ _?[0-9,]*[0-9]$ || "$pre" =~ [/:.].*$ ]] then if [[ "$COMP_LINE" =~ -s ]]; then : "$eff i $dep" elif [[ "$COMP_LINE" =~ -e ]]; then : "i $dep" else : "STRING" fi # These ifs here, though not perfect, seem good enough. else : "$eff i $dep" fi esac; mapfile -t COMPREPLY < <(compgen -W "$_" -- "$cur") W=$(# Completion words if [[ "$cur" =~ ^[0-9,]*[0-9]$ ]] # any effect then compgen -P "$cur" -W ", i $dep" elif [[ "$cur" =~ ^[0-9,]*[0-9],$ ]] # comma then compgen -P "$cur" -W "$eff" elif [[ "$cur" =~ ^[0-9,]*i$ || "$cur" =~ ^[0-9,]*i?/ ]] # 4-bit then compgen -P "${cur%%/*}/" -W "black red green yellow blue magenta cyan white" elif [[ "$cur" =~ ^[0-9,]*: ]] # 8-bit then compgen -P "${cur%%:*}:" -W "$(seq -w 0 255)" elif [[ "$cur" =~ ^[0-9,]*[.] ]] # 24-bit then compgen -P "${cur%%.*}." -W "$(ecos -=nam-dec-raw|cut -f1 -d:)" elif [[ "${cur: -1}" == "_" ]] # background then compgen -P "$cur" -W "$eff i $dep" elif [[ "${cur:0:2}" =~ -[Hh] ]] # -[Hh]FUN (dev opt) then compgen -P "${cur:0:2}" -W "$(ecos -=funs-list)" printf -- "%s\n" "${cur:0:2}" elif [[ "${cur:0:1}" == "-" ]] # any options then compgen -W "$ops" fi) if [[ "$W" ]]; then mapfile -t COMPREPLY < <(compgen -W "$W" -- "$cur"); fi;} ecos--funs-list() { : <<_ List internal functions that are completion candidates. Remove common ecos-- prefix. _ : "${BASH_SOURCE[0]}" if [[ "$_" ]] then <"$_" grep -oP '(?<=^ecos--).*(?=[(][)])' else declare -F | grep -oP '(?<= ecos--).*' fi ;} #⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ##### Get help ecos--doc() { : _fun <<_ Show a function's docstring. Colors. If FUN is empty, show the docstring of ecos. Otherwise, show the docstring of ecos--FUN. _ ecos--doc-no-colors "$@" | ecos--doc-colorize | \less -RF ;} ecos--doc-nc() { : _fun <<_ Show a function's docstring. No colors. If FUN is empty, show the docstring of ecos. Otherwise, show the docstring of ecos--FUN. _ ecos--doc-no-colors "$@" | \less -RF ;} ecos--doc-no-colors() { : _fun <<_ Output to stdout a function's docstring without colors. _ : ecos"${1:+--$1}"; type "$_" | sed -n "/<<_$/,/^_$/p" | sed "1 {s/ <<_.*/)/ ; s/[^:]*: */$_ (/}; $ d" ;} ecos--doc-colorize() { : <<_ Filter to colorize ecos' docstrings. _ ecos--filter 1.#fde028 '^[^ ]*' 1 . \ 1.orange1 '\(.*' 1 . \ 1.deepskyblue '.*' 2 . \ 1.#b4fa70 '^[A-Z][a-z]+:' 3,$ . \ 0.lightskyblue '^ *([|]|ecos).*' . . \ 1.darkslateblue '^_+$' . . ;} #⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ### Source it or run it case "$-" in *i*) : "Presumably being sourced by interactive shell." ;; * ) read -r REPLY _ < <(caller) if ((REPLY > 0)) then : "Presumably being sourced by another file." else : "Presumably being executed." set -eo pipefail ecos "$@" fi esac # Local Variables: # coding: utf-8 # indent-tabs-mode: nil # sentence-end-double-space: nil # outline-regexp: "###* " # End: ## Ecos ends here
📆 2026-W02-3📆 2026-01-07