Colors

ASCII 챠트에서 0x20 이하 문자들과 0x7F 는 A B C 1 2 3 와같이 화면에 표시되는 모양은 없지만 라인개행을 한다든지 backspace 로 문자를 삭제하는 등의 기능을 하는 control 문자입니다. 그 외 function 키나 방향키에 대한 값 들은 특수한 방법을 이용해 전송하는데요. 방법은 ascii 챠트에서 escape 문자에 해당하는 0x1b 문자를 먼저 보내고 뒤에 관련된 문자들(sequence)를 전송합니다 ( 그래서 escape sequence 라고 부릅니다 )

$ od -tax1 [enter]
^[OP^[OQ^[OR^[[A^[[B    # f1 ~ f3 키, 위 아래 방향키를 누르고 [enter]
0000000 esc   O   P esc   O   Q esc   O   R esc   [   A esc   [   B  nl
         1b  4f  50  1b  4f  51  1b  4f  52  1b  5b  41  1b  5b  42  0a
# ctrl-d 로 종료

위의 od 명령을 이용해 function 키와 방향키를 눌렀을때의 키값을 조회해 보면 제일앞에 0x1b (esc) 문자가 나오는것을 볼 수 있습니다. 이렇게 escape sequence 를 전송받은 프로그램은 나름대로 해석하여 처리하게 됩니다.

Color 표시를 위한 escape sequence

터미널에 color 를 표시할 때도 escape sequence 를 터미널 프로그램에 전송해 처리하게 하는데 작성하는데 간단한 규칙이 있습니다.

1. escape

escape 문자는 다음 중에 하나를 사용할 수 있습니다.

\e , \x1b (16진수) , \033 (8진수)

2. [

시작을 나타냅니다.

3. style; foreground; background

style 값은 여러개를 적을 수 있습니다.

4. m

종료를 나타냅니다.

사용예 )

# \e 문자 처리를 위해 echo 명령에서 -e 옵션을 사용합니다.
echo -e "\e[0;31m      red foreground"
echo -e "\e[0;44m      blue background"
echo -e "\e[0;1m       bright style"
echo -e "\e[0;31;44m   red foreground, blue background"
echo -e "\e[01;31;44m  bright style, red foreground, blue background"

# 다음에 이어지는 스트링에는 속성이 적용되지 않게 reset 합니다.
echo -e "\e[0m"

# 먼저 reset 한 후 속성을 적용합니다.
echo -e "\e[01;31m  ..."

Color, Style 속성값

다음은 16 color terminal 에 적용할 수 있는 값입니다. style 값은 10 보다 작고 foreground 값은 30 번대, background 값은 40 번대를 사용하는 것을 알 수 있습니다. 값만으로 구분이 가능하므로 순서에 상관없이 적을 수 있습니다.

# Style
0 — Reset all attributes  # 이전에 적용했던 속성을 reset 합니다.
1 — Bright
2 — Dim
4 — Underscore
5 — Blink
7 — Reverse     # foreground 와 background 색상이 서로 바뀌어 표시됩니다.
8 — Hidden      # 스트링이 보이지 않게됩니다.

# Foreground
30 — Black
31 — Red
32 — Green
33 — Yellow
34 — Blue
35 — Magenta
36 — Cyan
37 — White

# Background
40 — Black
41 — Red
42 — Green
43 — Yellow
44 — Blue
45 — Magenta
46 — Cyan
47 — White

스크립트로 전체 색상 프린트 해보기

#!/bin/bash

for fgcolor in {30..37} ; do 
    for bgcolor in {40..47}; do
        for attr in 0 1 2 4 5 7; do 
            echo -en "\e[${attr};${fgcolor};${bgcolor}m" 
            echo -n " ${attr};${fgcolor};${bgcolor} "
            echo -en "\e[0m"
        done
        echo
    done
    echo
done

-------------------------------------------------------
#!/bin/bash

for fgcolor in {30..37} {90..97} 39 ; do
    for bgcolor in {40..47} {100..107} 49 ; do
        for attr in 0 1 2 4 5 7 ; do
            echo -en "\e[${attr};${bgcolor};${fgcolor}m"
            echo -n " ${attr};${bgcolor};${fgcolor} "
            echo -en "\e[0m"
        done
        echo 
    done
done

tput 명령을 이용한 설정

실제 터미널 장치가 메인프레임에 연결되어 사용되던 과거에는 터미널의 종류가 한두 가지가 아니었을 것이라는 것은 쉽게 짐작할 수 있습니다. 각각의 터미널 장치들은 제공하는 기능도 달랐고 control characters, escape seqeunces 를 이용한 commands 들도 틀렸다고 합니다. 그래서 이와같은 터미널들의 정보를 라이브러리로 만들어놓은 것이 terminfo (termcap 의 새로운 버전) 입니다 (우분투의 경우 /lib/terminfo 에서 볼수있습니다). terminfo 라이브러리를 이용하면 device independent 한 방법으로 프로그램 할 수 있습니다.

위에서 처럼 color escape sequence 를 직접 하드코드하면 다른 종류의 터미널을 emulation 하는 프로그램에서는 동일하게 동작하지 않을 수 있습니다. 실제로 Ctrl-Alt-F1 ~ F6 에서 제공되는 리눅스 가상콘솔은 $TERM 값이 linux 이고 gnome-terminal 같은경우는 xterm 으로 gnome-terminal 에서 설정한 색상은 가상콘솔 에서 다르게 보입니다.

tput 명령으로 설정을 하면 현재 터미널의 terminfo 정보를 이용해 값을 설정하므로 터미널 종류가 다른 환경에서도 동일하게 적용될 수 있습니다.

# foreground color 적용
tput setaf {color숫자}

# background color 적용
tput setab {color숫자}

# Style 속성 적용
tput bold – Set bold mode
tput dim  – turn on half-bright mode
tput smul – begin underline mode
tput rmul – exit underline mode
tput rev  – Turn on reverse mode
tput smso – Enter standout mode (bold on rxvt)
tput rmso – Exit standout mode

tput sgr0 – Turn off all attributes ( '\e[0m' 와 같은 기능 )

# Color 값
0 – Black
1 – Red
2 – Green
3 – Yellow
4 – Blue
5 – Magenta
6 – Cyan
7 – White

tput 명령은 매 실행시마다 terminfo 라이브러리를 조회하므로 속도가 느리다는 단점이 있습니다. 그러므로 먼저 사용되는 속성값들을 변수에 저장해 놓고 사용하는 것이 좋습니다.

사용예 )

color_reset=$(tput sgr0)
color_f_red=$color_reset$(tput setaf 1)
color_b_blue=$color_reset$(tput setab 4)
color_s_bold=$color_reset$(tput bold)
color_my1=$color_reset$(tput setaf 1)$(tput setab 4)
color_my2=$color_reset$(tput bold)$(tput setaf 1)$(tput setab 4)

echo "$color_f_red    red foreground"
echo "$color_b_blue   blue background"
echo "$color_s_bold   bold style"
echo "$color_my1      red foreground, blue background"
echo "$color_my2      bold style, red foreground, blue background"

echo $color_reset

# 다음에 이어지는 스트링에는 속성이 적용되지 않게 reset 합니다.
$(tput sgr0)

# 먼저 reset 한 후 속성을 적용합니다.
$(tput sgr0)$(tput setaf 1) ...

스크립트로 전체 색상 프린트 해보기

#!/bin/bash

for fgcolor in {0..7} ; do
    for bgcolor in {0..7}; do
        for attr in sgr0 bold dim smul rev; do
            echo -n "$(tput $attr)$(tput setaf $fgcolor)$(tput setab $bgcolor)"
            echo -n " $attr;$fgcolor;$bgcolor "
            echo -n "$(tput sgr0)"
        done
        echo 
    done
    echo
done

Prompt 설정

Shell 이 $PS1 변수 값을 이용해 prompt 를 표시하는 과정을 eval echo \""$PS1"\" 명령으로 표현할 수 있는데 이때 eval 의 읽어들이는 단계를 거친 후 prompt escape sequences 가 처리되고 실행단계를 거친다고 볼 수 있습니다.

예를들어 이전 명령의 종료 상태 값을 prompt 에 표시하기 위해 PS1="hello \w $? > " 와 같이 double quotes 을 이용한다면 이때 이미 $? 변수값이 확장되어 대입된 상태가 되므로 항상 같은 값을 표시하게 됩니다. 그러므로 매번 prompt 가 표시될때 마다 새로운 $? 변수값이 표시되게 하려면 single quotes 을 사용하여 설정해야합니다.

AA=hello
PS1="$AA \w "'$?'" > "

또한 prompt escape sequences 중에 하나인 \$ 는 double quotes 에서 $ 로 escape 되므로 올바르게 적용하기 위해서는 single quotes 을 사용합니다.

AA=hello
PS1="$AA \w "'$? \$ '

color escape sequences 같은 non-printing 문자를 prompt 에 사용할 경우는 항상 \[ \] 으로 감싸줘야 커서의 포지션이 올바르게 설정됩니다.

AA=hello

color_reset="\[\e[0m\]"
color_f_red="\[\e[0;31m\]"

PS1="$AA \w $color_f_red"'$?'"$color_reset"' \$ '
-------------------------------------------------

color_reset="\[$(tput sgr0)\]"
color_f_red="$color_reset\[$(tput setaf 1)\]"

PS1="$AA \w $color_f_red"'$?'"$color_reset"' \$ '

Prompt Escape Sequences

Code Description
\a an ASCII bell (07) 문자
\d "Weekday Month Date" 포멧의 날짜 (e.g., "Tue May 26")
\D{format} strftime(3) 함수에서 format 스트링이 사용되고 결과가 표시됩니다. { } 는 꼭 사용해야 되며 \D{} 와같이 empty 일 경우는 현재 로케일 설정에 따라 시간이 표시됩니다.
\e an ASCII escape (033) 문자
\h the hostname 에서 첫번째 ' . ' 까지의 부분
\H the hostname
\j 현재 background jobs 의 개수
\l 현재 shell 의 terminal device 이름 (tty5)
\n newline
\r carriage return
\s 현재 shell 이름 ( the basename of $0 )
\t 현재 시간 ( 24-hour HH:MM:SS 포멧 )
\T 현재 시간 ( 12-hour HH:MM:SS 포멧 )
\@ 현재 시간 ( 12-hour am/pm 포멧 )
\A 현재 시간 ( 24-hour HH:MM 포멧 )
\u 현재 사용자의 username
\v the version of bash (e.g., 2.00)
\V the release of bash, version + patch level (e.g., 2.00.0)
\w 현재 working directory ( $HOME 은 ' ~ ' 로 표시 )
\W 현재 working directory 에서 basename 만 표시 ( $HOME 은 ' ~ ' 로 표시 )
\! 이번 명령의 history number
\# 이번 명령의 command number
( 터미널을 열고 enter 로 명령을 사용할때 마다 하나씩 올라감 )
\$ effective UID 가 0 이면 '#' 그렇지 않으면 '$'
\nnn 8 진수 문자
\\ a backslash
\[ non-printing 문자 sequence 의 시작
( 가령 color escape sequence 를 적용할때 사용해야됨 )
\] non-printing 문자 sequence 의 종료