Skip to content

Instantly share code, notes, and snippets.

@aileftech
Created October 1, 2022 18:10
Show Gist options
  • Star 122 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save aileftech/dd4f5598b1f3837651fdf16e5abc3ffb to your computer and use it in GitHub Desktop.
Save aileftech/dd4f5598b1f3837651fdf16e5abc3ffb to your computer and use it in GitHub Desktop.
A Bash one-liner to produce a list of HEX color codes that read like (supposedly) valid English words
$ grep -P "^[ABCDEFabcdefOoIi]{6,6}$" /usr/share/dict/words | tr 'OoIi' '0011' | tr '[:lower:]' '[:upper:]' | awk '{print "#" $0}'
#ACAD1A
#B0BB1E
#DEBB1E
#AB1DED
#ACAC1A
#ACCEDE
#AC1D1C
#BAB1ED
#BA0BAB
#BEADED
#BEDDED
#BEEFED
#B0BBED
#B0D1CE
#B00BED
#CABBED
#CABB1E
#CADD1E
#C1CADA
#C0DDED
#C0FFEE
#C01FED
#DABBED
#DECADE
#DEC1DE
#DEC0DE
#DEEDED
#DEFACE
#DEF1ED
#DE1CED
#D0FFED
#D00DAD
#EDD1ED
#EFFACE
#FACADE
#F1BBED
#F0BBED
#0FF1CE
@antler5
Copy link

antler5 commented Oct 1, 2022

For those of you with 256color terminals, here's one that shows each color:

sed -n 's/^[a-fios]\{6\}$/\U&/p' /usr/share/dict/words \
  | tr 'OIS' '015' \
  | gawk --non-decimal-data \
         '{print "\033[48;2;" \
                 ("0x"substr($0,1,2))+0 ";" \
                 ("0x"substr($0,3,2))+0 ";" \
                 ("0x"substr($0,5,2))+0 "m" \
                 "       \033[0m " \
                 $0}'

I thought about leaving the text inside each color block, but I'd want to invert the color for the text FG, which would take us even further from a "one-liner". I'm already wishing I could loop over each color-component inside the pipe, but I'd need to keep the hex form for the output:

# Save HEX, using it to replace awk's $0 at the end: 
sed -n 's/^[a-fios]\{6\}$/\U&/p' /usr/share/dict/words \
  | tr 'OIS' '015' \
  | { while read HEX; do \
        echo $HEX | fold -w2 | xargs -n3 bash -c 'printf "\033[48;2;%03d;%03d;%03dm      \033[0m '"$HEX"'\n" 0x$0 0x$1 0x$2'; \
      done; }

# Loop over components instead of passing printf 3 args, now without invoking bash:
sed -n 's/^[a-fios]\{6\}$/\U&/p' /usr/share/dict/words \
  | tr 'OIS' '015' \
  | { while read HEX; do \
        printf '\033[48;2;'; \
        echo $HEX | fold -w2 | xargs -I {} printf '%03d' 0x{} | fold -w3 | tr '\n' ';'
        printf 'm      \033[0m %s\n' "$HEX"; \
      done; }

# Re-introduce that messy sub-shell for fancy FG colors:
sed -n 's/^[a-fios]\{6\}$/\U&/p' /usr/share/dict/words \
  | tr 'OIS' '015' \
  | { while read HEX; do \
        printf '\033[48;2;'; \
        echo $HEX | fold -w2 | xargs -I {} printf '%03d' 0x{} | fold -w3 | tr '\n' ';'
        printf 'm\033[38;2;'; \
        echo $HEX | fold -w2 | xargs -I {} bash -c 'DEC=$(printf "%d" 0x'{}'); printf "%03d" "$((255 - DEC))"' | fold -w3 | tr '\n' ';'
        printf 'm %s \033[0m %s\n' "$HEX"; \
      done; }

I bet someone who knows Perl well could do an all-in-one.

@gabrielsroka
Copy link

as html, live

<div id=app></div>
<script src='https://gabrielsroka.github.io/webpages/scripts/enable1.js'></script>
<script>
/*
`words` is defined in enable1.js.
see also 
https://github.com/gabrielsroka/gabrielsroka.github.io/blob/master/webpages/scripts/enable1.js
https://code.google.com/archive/p/dotnetperls-controls/downloads
*/
app.innerHTML = words
    .filter(w => w.match(/^[a-fios]{6}$/))
    .map(c => `<div style='background-color: #${c.replace(/o/g, '0').replace(/i/g, '1').replace(/s/g, '5')}; width: 100px'>#${c}</div>`)
    .join('');
</script>

@jaysoffian
Copy link

jaysoffian commented Oct 1, 2022

All in bash:

#!/usr/bin/env bash
shopt -s nocasematch
while read -r word; do
    if [[ $word =~ ^[abcdefoi]{6,6}$ ]]; then
        word=${word//o/0}
        word=${word//i/1}
        word=${word^^}
        printf '#%s\n' "$word"
    fi
done < /usr/share/dict/words

@karakfa
Copy link

karakfa commented Oct 2, 2022

here is an all awk version, slightly more readable than the sed version

awk '{$0=toupper($0)} /^[A-FOI]{6,6}$/{gsub("I",1); gsub("O",0); print "#" $0}' file

@antler5
Copy link

antler5 commented Oct 2, 2022

Pretty gosh darn happy with this now c:

Not pure bash, I think that the coreutils's conciseness (tr, fold, xargs) is indispensable and grep is just so much faster (something does feel a little slow, donno if it's the sub-shells, or the arrays, haven't put my finger on it). I think it's concise, and clear... so long as one knows <<<, <(), IFS, and overlooks the entire BG_RGB pipeline (for which one ought to know a relatively obscure feature of printf, xargs, and (new to me) fold). Gotta love shell.

while read -r WORD; do
  while read -r HEX; do
    IFS=" "
    read -r -a BG_RGB <<< "$(fold -w2 <<< $HEX | xargs -I {} printf '%d ' 0x{})"
    read -r -a FG_RGB <<< "$(for component in ${BG_RGB[*]}; do printf '%d ' $((255 - $component)); done)"
    IFS=";"
    printf '\033[48;2;'"${BG_RGB[*]}"'m\033[38;2;'"${FG_RGB[*]}"'m '"$HEX"' \033[0m %s\n'
  done < <(tr 'IOS' '105' <<< ${WORD^^})
done < <(grep -Pi "^[A-FIOS]{6,6}$" /usr/share/dict/words)
# time for a in {1..10}; do ... ; done
# real  0m7.699s
# user  0m8.047s
# sys   0m1.301s

Edit:

something does feel a little slow

It was xargs, 'bout 50% faster without it (specifically not because of any other change I made to refactor it out, though most tweaks do push it over real 0m4s).

while read -r WORD; do
  while read -r HEX; do
    IFS=$'\n'
    BG_RGB=( $(fold -w2 <<< $HEX | while read -r COMP; do printf '%d\n' 0x$COMP; done) )
    FG_RGB=( $(for COMP in ${BG_RGB[*]}; do printf '%d\n' $((255 - $COMP)); done) )
    IFS=";"
    printf '\033[48;2;'"${BG_RGB[*]}"'m\033[38;2;'"${FG_RGB[*]}"'m '"$HEX"' \033[0m %s\n'
  done < <(tr 'IOS' '105' <<< ${WORD^^})
done < <(grep -Pi "^[A-FIOS]{6,6}$" /usr/share/dict/words)
# time for a in {1..10}; do ... ; done
# real  0m3.908s
# user  0m3.711s
# sys   0m0.716s

Edit: sed is slightly faster than grep or awk (for matching words, on my machine), and dropping the nested while loop into what is now sed's pipeline saves a ton of time. This is as fast as I think it gets, at least without using awk or sed for more than just what grep can do.

while read -r HEX; do
  IFS=$'\n'
  BG_RGB=( $(fold -w2 <<< $HEX | while read -r COMP; do printf '%d\n' 0x$COMP; done) )
  FG_RGB=( $(for COMP in ${BG_RGB[*]}; do printf '%d\n' $((255 - $COMP)); done) )
  IFS=";"
  printf '\033[48;2;'"${BG_RGB[*]}"'m\033[38;2;'"${FG_RGB[*]}"'m '"$HEX"' \033[0m %s\n'
done < <(sed -n 's/^[A-FIOSa-fios]\{6\}$/\U&/p' /usr/share/dict/words | tr 'IOS' '105') \
# time for a in {1..10}; do ... ; done
# real	0m2.318s
# user	0m2.227s
# sys	0m0.464s

Edit: I gotta go to bed :c
But just as a quick and dirty point of comparison, has an (extra) advantage bc the loop is unrolled (not that it makes a difference):

#!/usr/bin/env -S awk --non-decimal-data -f
{$0=toupper($0)}
/^[A-FIOS]{6,6}$/ {
  gsub("I",1);
  gsub("O",0);
  gsub("S",5);
  print "\033[48;2;" \
        000+("0x"substr($0,1,2)) ";" \
        000+("0x"substr($0,3,2)) ";" \
        000+("0x"substr($0,5,2)) "m" \
        "\033[38;2;" \
        255-("0x"substr($0,1,2)) ";" \
        255-("0x"substr($0,3,2)) ";" \
        255-("0x"substr($0,5,2)) "m" \
        " #" $0 \
        " \033[0m"; }
# time for a in {1..10}; do ... ; done
# real	0m0.539s
# user	0m0.532s
# sys	0m0.008s

@dainiuxt
Copy link

dainiuxt commented Oct 2, 2022

Where is #BADA55 (or this is clean version)?

@gabrielsroka
Copy link

@reverofevil
Copy link

image

@lucasqueiroz
Copy link

What if you take into account hex codes with 3 characters?

@mcjcloud
Copy link

image

@lucasqueiroz

#AAA
#ABC
#ABE
#ACE
#ADA
#ADD
#AD0
#A1D
#A55
#BAD
#BBC
#BED
#BEE
#B1B
#B1D
#B0A
#B0B
#B00
#B5C
#CAB
#CAD
#CB1
#C1D
#C0B
#C0D
#C00
#C05
#DAB
#DAD
#DEB
#DFC
#D1D
#D1E
#D0C
#D0E
#D5C
#EBB
#E5C
#E5E
#FAD
#FED
#FEE
#F1B
#F0B
#F0E
#1B1
#1CE
#1DA
#111
#0AF
#0DD
#0DE
#0ED
#0FF
#5AD
#5EA
#5EC
#5EE
#51C
#50B
#50D
#55E

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment