初歩のシェルスクリプトで遊ぶ[端末の256色カラーセレクタ(5)]
16色版v4.0
ファイルひとつにまるごとコピーして、実行権限つけて端末上で実行、でいける、といいけれど。
256色版の簡易版ではありません。16色と256色では、色の指定方法が異なるので。
Ubuntu20.04 xfce-terminal
無料・無保証で構わない人のみどうぞ。
#!/bin/sh #!/usr/bin/zsh #!/usr/bin/ksh #!/usr/bin/posh #!/bin/bash #!/bin/busybox sh #!/usr/bin/yash # ======================================================================================== # ■ 端末カラーセレクター16色 v4.0 # https://twitter.com/beta_reverse_2 # ======================================================================================== # ▼ 操作 # 上キー-下キー,8-2:フォーカス移動 # 左キー-右キー,4-6:色選択のとき 色を選択決定 # 文字装飾のとき フォーカス左右移動 # スペース,0:文字装飾のとき フォーカスの値を有効/無効切替 # /,q,Ctrl+C:終了 # s,-:メインサンプルとサブサンプルの色を入れ替える # * :フリーサンプル文字を書き換える # # ▼ 動作するシェル #!/bin/dash # # ▼ 簡単にテスト済み #!/bin/bash #!/bin/busybox sh #!/usr/bin/yash #!/usr/bin/zsh # # ▼ 動作はするが非推奨_遅い #!/usr/bin/ksh #!/usr/bin/posh # # ▼ 動かない #!/usr/bin/fish # # ▼ 既知の不具合 # カーソルキーを押しっぱなしにしたときなど、入力キーが端末に漏れて表示される。 # # cd "$(dirname "`readlink -f "${0}"`" )" # ====[shMerge<disable>]==== #_shMergeDisabled # PATH="./lib:${PATH}" # ====[shMerge<disable>]==== #_shMergeDisabled # ************************************************************************************** # ユーザ設定_任意 # ************************************************************************************** # サンプルテキスト # 横幅推奨値=' ' STD_SAMPLE=' standard sample text ' MAIN_SAMPLE=' main sample text ' SUB_SAMPLE=' sub sample text ' FREE_SAMPLE=' free sample text ' # # ====[shMerge]==== effectCodeList16sh 20201101_090836 effectCodeList16sh(){ ( # ====[shMerge<ins>]==== #!/bin/sh # 16色版の文字装飾コード # このシェル変数を書き換えるだけで、カスタマイズ可能。 # 改行区切りで数字を追加削除すること。 LF=' ' codeList="0${LF}1${LF}2${LF}3${LF}4${LF}5 6${LF}7${LF}8${LF}9${LF}10${LF}11 12${LF}13${LF}14${LF}15${LF}16${LF}17 18${LF}19${LF}20${LF}21${LF}22${LF}23 24${LF}25${LF}26${LF}27${LF}28${LF}29 51${LF}52${LF}53${LF}54${LF}55${LF}56 60${LF}61${LF}62${LF}63${LF}64${LF}65" echo "${codeList}" ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== helpMessageC16sh 20201101_090836 helpMessageC16sh(){ ( # ====[shMerge<ins>]==== #!/bin/sh # 操作説明_不要ならtrueのみに printf \ '\033[7m%s\033[27m %s '\ '\033[7m%s\033[27m %s '\ '\033[7m%s\033[27m %s\n'\ '\033[7m%s\033[27m %s '\ '\033[7m%s\033[27m %s '\ '\033[7m%s\033[27m %s '\ '\033[7m%s\033[27m %s\n' \ ' up down ' 'フォーカス切替' \ ' left right ' '選択決定,選択' \ ' space 0 ' 'チェック' \ ' + a ' 'コード表示'\ ' - s ' 'mainとsubを入替' \ ' * ' 'サンプル書換' \ ' / q ' '終了' true ) } # ====[shMerge</ins>]==== # ====================================================================================== # シェル関数_外部へ分離可能 # ====================================================================================== # # ====[shMerge]==== pressKeySh 20201101_090836 pressKeySh(){ ( # ====[shMerge<ins>]==== #!/bin/sh # キー入力を1回のみ受け取る stty_BAK=`stty -g` stty -echo raw dd count=1 2>/dev/null stty "${stty_BAK}" ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== pressKeyScan16sh 20201101_090836 pressKeyScan16sh(){ ( # ====[shMerge<ins>]==== #!/bin/sh focusID=${1:-1} # 操作キー取得 # eval `printf 'ESC=\033 ctrl_plus_C=\003 '` # 【gloval const ESC ctrl_plus_C】# ====[shMerge<disable>]==== #_shMergeDisabled UP____KEY="${ESC}[A" DOWN__KEY="${ESC}[B" RIGHT_KEY="${ESC}[C" LEFT__KEY="${ESC}[D" SPACE_KEY=' ' TAB______KEY=' ' PAGE_UP___KEY="${ESC}[5~" PAGE_DOWN_KEY="${ESC}[6~" stty_BAK=`stty -g` stty -echo raw while true ; do case "`dd count=1 2>/dev/null`" in # ---------------------------------------------------------------------- ( 6 | "${RIGHT_KEY}" ) # ---------------------------------------------------------------------- case "${focusID}" in ( 0 ) echo '0__RIGHT' break 1 ;; ( 1 ) echo '1__RIGHT' break 1 ;; ( 2 ) echo '2__RIGHT' break 1 ;; esac ;; # ---------------------------------------------------------------------- ( 4 | "${LEFT__KEY}" ) # ---------------------------------------------------------------------- case "${focusID}" in ( 0 ) echo '0__LEFT' break 1 ;; ( 1 ) echo '1__LEFT' break 1 ;; ( 2 ) echo '2__LEFT' break 1 ;; esac ;; # ---------------------------------------------------------------------- ( 8 | "${UP____KEY}" | "${PAGE_UP___KEY}" ) # ---------------------------------------------------------------------- echo '_UP' break 1 ;; # ---------------------------------------------------------------------- ( 2 | "${DOWN__KEY}" | "${TAB______KEY}" | "${PAGE_DOWN_KEY}" ) # ---------------------------------------------------------------------- echo '_DOWN' break 1 ;; # ---------------------------------------------------------------------- ( s | '-' ) # ---------------------------------------------------------------------- echo '_MINUS' break 1 ;; # ---------------------------------------------------------------------- ( 0 | "${SPACE_KEY}" ) # ---------------------------------------------------------------------- case "${focusID}" in ( 0 ) true ;; ( 1 ) true ;; ( 2 ) echo '2__SPACE' break 1 ;; esac ;; # ---------------------------------------------------------------------- ( '*' ) # ---------------------------------------------------------------------- echo '_ASTER' break 1 ;; # ---------------------------------------------------------------------- ( '+' | a ) # ---------------------------------------------------------------------- echo '_PLUS' break 1 ;; # ---------------------------------------------------------------------- ( '/' | 'q' | "${ctrl_plus_C}" ) # ---------------------------------------------------------------------- echo '_QUIT' break 1 ;; esac done stty "${stty_BAK}" exit 0 ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== colorRadioSh 20201101_090836 colorRadioSh(){ ( # ====[shMerge<ins>]==== #!/bin/sh # カラーサンプルとラジオボタン TARGET="${1}" # 0-15 NN=-1 # countUp # ESC=`printf '\033'` # 【gloval const ESC】# ====[shMerge<disable>]==== #_shMergeDisabled # TARGET 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # NN 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 for LL in 40 41 42 43 44 45 46 47 0 100 101 102 103 104 105 106 107 0 ; do # 「0」改行値を出力 if test "${LL}" -eq 0 ; then echo "${ESC}[0m" continue fi NN=$(( ${NN} + 1 )) case ${TARGET} in ( ${NN} ) printf "\033[${LL}m%s"'\033[0m\033[7m%s\033[0m'"\033[${LL}m%s" ' ' '*' ' ' ;; ( * ) printf "\033[${LL}m%s" ' ' ;; esac done ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== overWriteFocusMarkSedSh 20201101_090836 overWriteFocusMarkSedSh(){ ( # ====[shMerge<ins>]==== #!/bin/sh focusNo="${1}" #printf '\033[1;1H''\033[?25l' sed --posix -n -e '#n # 1行目行頭にエスケープシーケンス埋め込み 1{ s/^/\o033[1;1H\o033[?25l/ } # 行末改行前にエスケープシーケンス埋め込み ${ s/$/\o033[?0J\o033[?25h/ } # フォーカスしているならば /^FOCUS_ID_'${focusNo}'$/{ # 上書きして次の行を読み込む n # 矢印を右端に付加 s/$/ \o033[7m <= \o033[0m/ # ↓のフォーカスID削除には該当せず通常出力へ } /^FOCUS_ID_/ { # フォーカスID行を出力せずにcontinue d } # フォーカスIDではない他の全行ならば { s/$/\o033[0K/ # 行末尾に行右側消去を埋め込み p # 出力 } ' #printf '\033[?0J''\033[?25h' ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== transFgNo 20201101_090836 transFgNo(){ ( # ====[shMerge<ins>]==== #!/bin/sh # 色番号とSGRの番号を変換する # ${1}:数値 # 0-7 => 30-37 if test "${1}" -ge 0 && test "${1}" -le 7 ; then echo $(( ${1} + 30 )) # 8-15 => 90-97 elif test "${1}" -ge 8 && test "${1}" -le 15 ; then echo $(( ${1} + 82 )) fi ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== transBgNo 20201101_090836 transBgNo(){ ( # ====[shMerge<ins>]==== #!/bin/sh # ${1}:数値 # 0-7 => 40-47 if test "${1}" -ge 0 && test "${1}" -le 7 ; then echo $(( ${1} + 40 )) # 8-15 => 100-107 elif test "${1}" -ge 8 && test "${1}" -le 15 ; then echo $(( ${1} + 92 )) fi ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== effectCodeBuildSh 20201101_090836 effectCodeBuildSh(){ ( # ====[shMerge<ins>]==== #!/bin/sh # 実用できる文字装飾コードを組み立て eval 'set -- '${1} IFS=';' echo "${*}" ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== effectCodeCheckSh 20201101_090836 effectCodeCheckSh(){ ( # ====[shMerge<ins>]==== #!/bin/sh #!/usr/bin/zsh # 文字装飾コードのチェックシートを表示する # ESC=`printf '\033'` # 【gloval const ESC】# ====[shMerge<disable>]==== #_shMergeDisabled # ${1}:フォーカスのID値(${2}のデータの行番号-1) focusIdNo=${1} # コードリストの行番号(${1}で指定される数値) codeIdCount='-1' # forループ1回ごとに+1 # 出力は1行ずつechoで echoLine='' # ${2}:コードの番号*1000 のリスト 有効/無効(正/負) # コマンド置換で「-1000 LF -1001 LF 1002 LF 1-1003 ....」の改行を空白に変換(zsh) set -- `echo ${2}` for flagNo in ${@} ; do codeIdCount=$(( ${codeIdCount} + 1 )) codeNo=$(( ${flagNo#-} - 1000 )) # 表示文字と色も変数に格納しておく # コードが有効化されているならば case "${flagNo}" in ( -* ) Ast=' ' ;; ( * ) Ast="${ESC}[7m*${ESC}[27m" ;; esac # 表示文字と色も変数に格納しておく # フォーカスが当たっているならば case ${focusIdNo} in ( ${codeIdCount} ) brL="${ESC}[7m[${ESC}[27m" brR="${ESC}[7m]${ESC}[27m" ;; ( * ) brL=' ' brR=' ' ;; esac case "${codeNo}" in # 桁数に合わせて横幅調整 ( ? ) # 1文字ならば echoLine="${echoLine}${brL}${Ast}${brR} ${codeNo}:${ESC}[${codeNo}mCode ${codeNo}${ESC}[0m" ;; ( * ) # 2文字以上ならば echoLine="${echoLine}${brL}${Ast}${brR}${codeNo}:${ESC}[${codeNo}mCode${codeNo}${ESC}[0m" ;; esac if test $(( ( ${codeIdCount} + 1 ) % 6 )) -eq 0 ; then echo "${echoLine}" echoLine='' fi done # 16色版は6の倍数以外もサポートする if test -n "${echoLine}" then echo "${echoLine}" fi ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== effectCodeFilterSh 20201101_090836 effectCodeFilterSh(){ ( # ====[shMerge<ins>]==== #!/bin/sh # ${1}:1;2;3;4... # stdinからの文字列に装飾を行う sed -e "{ s/^/\o033[${1}m/1 }" ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== effectCodeInversionSh 20201101_090836 effectCodeInversionSh(){ ( # ====[shMerge<ins>]==== #!/bin/sh # 指定行の数値を、正負反転させる NN=$(( ${1} + 1 )) sed -n -e "#n ${NN}{ s/^-/m/ s/^[^m]/-&/ s/^m// } { p } " ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== effectCodeRemoveSh 20201101_090836 effectCodeRemoveSh(){ ( # ====[shMerge<ins>]==== #!/bin/sh #!/usr/bin/zsh # 文字装飾コードを有効リストから削除する # ${1}:'1' # ${2}以降:' 2 15 1 3 6' # output:'2 15 3 6' MM=${1} ; shift 1 LL='' for NN in ${@} ; do if test ${NN} -eq ${MM} then true else LL="${LL} ${NN}" fi done echo ${LL} ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== color16codeBuildSh 20201101_090836 color16codeBuildSh(){ ( # ====[shMerge<ins>]==== #!/bin/sh # 16色指定の色とテキスト装飾のコードを組み立てる # 「CSI Pn m」の形式で # 前景色と背景色の色指定 # 色コードそのものではなく「0-15」で指定されている # fgとbgでそれぞれ作成する番号は異なる fgColorNo="${1:-7}" bgColorNo="${2:-0}" # 文字装飾分のコードが半角空白区切りでリストされる # 順番を維持すること effectCodeList="${3}" # 0-7 なら 30-37 if test "${fgColorNo}" -le 7 ; then fgColorID=$(( ${fgColorNo} + 30 )) else # 8-15 なら 90-97 fgColorID=$(( ${fgColorNo} + 82 )) fi # 0-7 なら 40-47 if test "${bgColorNo}" -le 7 ; then bgColorID=$(( ${bgColorNo} + 40 )) else # 8-15 なら 100-107 bgColorID=$(( ${bgColorNo} + 92 )) fi # 「;」で繋げる sgrList=`{ echo ${fgColorID} echo ${bgColorID} echo ${effectCodeList} | tr ' ' '\n' } | tr -s '\n' | paste -s -d ';'` # echoで出力するとdashで「\033」がエスケープ扱いされる printf '%s\n' '\033['"${sgrList}"'m' # [EOF] ) } # ====[shMerge</ins>]==== # ====================================================================================== # シェル関数_外部へ分離不可 # ====================================================================================== keyActionFunc__UP(){ # フォーカス切替(-1) focusID=$(( ( ${focusID} - 1 + 3 ) % 3 )) } keyActionFunc__DOWN(){ # フォーカス切替(+1) focusID=$(( ( ${focusID} + 1 + 3 ) % 3 )) } keyActionFunc__MINUS(){ # main と sub を入れ替え NN="${mainFgNo}" MM="${mainBgNo}" mainFgNo="${subFgNo}" mainBgNo="${subBgNo}" subFgNo="${NN}" subBgNo="${MM}" } keyActionFunc_0__RIGHT(){ # 値やフォーカスを+1 mainFgNo=$(( ( ${mainFgNo} + 1 + 16 ) % 16 )) } keyActionFunc_1__RIGHT(){ # 値やフォーカスを+1 mainBgNo=$(( ( ${mainBgNo} + 1 + 16 ) % 16 )) } keyActionFunc_2__RIGHT(){ # 値やフォーカスを+1 effectCodeFocus=$(( ( ${effectCodeFocus} + 1 + ${effectCodeWordCount} ) % ${effectCodeWordCount} )) } keyActionFunc_0__LEFT(){ # 値やフォーカスを-1 mainFgNo=$(( ( ${mainFgNo} - 1 + 16 ) % 16 )) } keyActionFunc_1__LEFT(){ # 値やフォーカスを-1 mainBgNo=$(( ( ${mainBgNo} - 1 + 16 ) % 16 )) } keyActionFunc_2__LEFT(){ # 値やフォーカスを-1 effectCodeFocus=$(( ( ${effectCodeFocus} - 1 + ${effectCodeWordCount} ) % ${effectCodeWordCount} )) } keyActionFunc_2__SPACE(){ # 文字装飾コードを有効/無効 # idフラグ番号を取得_$((id + 1000 * -1)) MM=`echo "${effectCodeFlags}" | sed -n -e "$(( ${effectCodeFocus} + 1 ))p"` # id番号を取得_マイナスがあれば取ってから1000を引く NN=$(( ${MM#-} - 1000 )) # チェックフラグの有効無効を反転 effectCodeFlags="`echo "${effectCodeFlags}" | effectCodeInversionSh ${effectCodeFocus}`" if test ${MM} -le -1 ; then # 現在無効ならば、有効リストに空白区切りで追加する effectCodeList="${effectCodeList} ${NN}" else # 現在有効ならば、有効リストから削除する eval 'set -- '${effectCodeList} effectCodeList=`effectCodeRemoveSh ${NN} ${@}` fi } keyActionFunc__ASTER(){ echo 'サンプル文字を変更します。' 1>&2 read FREE_SAMPLE if test -z "${FREE_SAMPLE}" then FREE_SAMPLE=' ' fi } keyActionFunc__PLUS(){ color16codeBuildSh "${mainFgNo}" "${mainBgNo}" "${effectCodeList}" color16codeBuildSh "${subFgNo}" "${subBgNo}" "${effectCodeList}" pressKeySh 1>/dev/null } keyActionFunc__QUIT(){ # 終了する stty echo exit 0 } # デバッグ用 debug1Sub(){ ( for XX in mainFgNo mainBgNo subFgNo subBgNo focusID effectCodeFocus ; do eval 'echo "${XX}:${'${XX}'}"' done ) } debug2Sub(){ ( echo "${effectCodeList}" echo "${effectCodeFlags}" | column -x ) } # ====================================================================================== # グローバル定数 シェル関数内での再定義と検索タグは必須 # ====================================================================================== ESC=`printf '\033'` # 【gloval const ESC】 ctrl_plus_C=`printf '\003'` # 【gloval const ctrl_plus_C】 # ====================================================================================== # スタートアップ処理 # ====================================================================================== # サンプル色[0-15] mainFgNo=15 mainBgNo=0 subFgNo=0 subBgNo=15 # 操作対象 focusID=0 # 文字装飾コードチェックシート カーソル位置 effectCodeFocus='0' # 文字装飾コードの総数( = チェックボックス数) if ! XX=`effectCodeList16sh` ; then exit 127 ; fi effectCodeWordCount=`echo "${XX}" | wc -l` # 文字装飾コードからフラグ用定数を作成 # 1000を足して負にしたもの(「0」に正負をつけるため) effectCodeFlags=`effectCodeList16sh | while read NN ; do echo '-'$(( ${NN} + 1000 )) done` # テキスト装飾コード有効リスト # 有効にした「順番に」改行区切りでコード番号が入る # 「1,2,3」と「2,3,1」は異なる effectCodeList='' # エコーバックを切る # キーを押しっぱなしのとき、ちらつく # 端末エミュレータによっては、終了しても設定が残る #stty -echo # ====================================================================================== # メインループ # ====================================================================================== while true ; do # 色番号をCSIコードに変換 # 16色システムカラーは前景と背景で番号が異なるため mainSampFgColor=`transFgNo ${mainFgNo}` mainSampBgColor=`transBgNo ${mainBgNo}` subSampFgColor=`transFgNo ${subFgNo}` subSampBgColor=`transBgNo ${subBgNo}` # テキスト装飾コードを構築 EFFECT_CODE=`effectCodeBuildSh "${effectCodeList}"` { # overWriteブロック { # 表示サンプルブロック echo "${ESC}[${EFFECT_CODE}m${ESC}[39;49m${STD_SAMPLE}${ESC}[0m 39;49 ${EFFECT_CODE}" echo "${ESC}[${EFFECT_CODE}m${ESC}[${mainSampFgColor};${mainSampBgColor}m${MAIN_SAMPLE}${ESC}[0m\ ${mainSampFgColor};${mainSampBgColor} ${EFFECT_CODE}" echo "${ESC}[${EFFECT_CODE}m${ESC}[${subSampFgColor};${subSampBgColor}m${SUB_SAMPLE}${ESC}[0m\ ${subSampFgColor};${subSampBgColor} ${EFFECT_CODE}" echo "${ESC}[${EFFECT_CODE}m${ESC}[${mainSampFgColor};${mainSampBgColor}m${FREE_SAMPLE}${ESC}[0m" echo "${ESC}[${EFFECT_CODE}m${ESC}[${subSampFgColor};${subSampBgColor}m${FREE_SAMPLE}${ESC}[0m" } echo 'FG 30 31 32 33 34 35 36 37' echo 'FOCUS_ID_0' colorRadioSh ${mainFgNo} echo 'FG 90 91 92 93 94 95 96 97' echo 'BG 40 41 42 43 44 45 46 47' echo 'FOCUS_ID_1' colorRadioSh ${mainBgNo} echo 'BG 100 101 102 103 104 105 106 107' echo 'FOCUS_ID_2' effectCodeCheckSh ${effectCodeFocus} "${effectCodeFlags}" helpMessageC16sh } | overWriteFocusMarkSedSh ${focusID} #debug1Sub # 【debug】 #debug2Sub # 【debug】 PRESS_KEY=`pressKeyScan16sh ${focusID}` keyActionFunc_${PRESS_KEY} done exit 255 # EOF