자바스크립트를 활성화 해주세요

macOS에서 ls 색상을 리눅스와 동일하게 변경하기

 ·  ☕ 11 min read

macOS에서 터미널을 사용할 때 차이점

macOS에서 터미널을 사용하다 보면 ls의 색이 리눅스 터미널을 사용하던 것과 다른 색으로 출력되는 것을 볼 수 있다.

디렉토리가 하늘색, 실행 파일이 빨간색, 심볼릭 링크의 상태 구분 불가
디렉토리가 파란색, 실행 파일이 초록색, 심볼릭 링크의 상태가 구분됨, 압축 파일이 주황색

파일/디렉토리 특성에 따라 다른 색상으로 분류되기만 해도 도움은 되지만, 웬만하면 일관된 색 배치로 보는 것이 더 편하기 때문에 색상을 동일하게 나타나게 하는 방법에 대해 찾아봤다.

ls의 색상이 다른 이유

처음에는 색상이 다른 이유가 애플이 또 평소처럼 지들 멋대로 표준화해서 그런 것이라 생각했다.

하지만 위키피디아의 ls 문서에서 해당 원인을 찾을 수 있었다.

It is frequently possible to highlight different types of files with different colors, instead of with characters as -F would. This is an area where the two main ls versions differ:

  • GNU ls uses the –color option; checks the Unix file type, the file permissions and the file extension and uses its own database to control colors maintained using dircolors.
  • FreeBSD ls uses the -G option; checks only the Unix file type and file permissions. and uses the termcap database.

번역하자면 다음과 같다.

대부분의 ls에서는 다른 파일 타입에 대해 다른 색상으로 하이라이트 하는 것이 가능하다. (-F 옵션을 사용하여 파일 타입에 따른 글자를 추가로 표현하는 방식도 있다.) 두가지 주류 ls 버전은 색상 하이라이트 부분에서 차이가 난다.

  • GNU ls는 --color 옵션을 사용한다. 유닉스 파일 타입, 파일 권한 및 파일 확장자를 확인하며, dircolors의 데이터베이스를 통해 색상이 관리된다.
  • FreeBSD ls는 -G 옵션을 사용한다. 유닉스 파일 타입과 파일 권한만 확인하며, termcap의 데이터베이스를 통해 색상이 관리된다.

macOS는 내부적으로 Darwin을 사용하며, 내부적으로 BSD 커널과 Mach 커널을 사용한다.

리눅스에서는 GNU ls를 사용하고, macOS에서는 FreeBSD ls를 사용했다고 볼 수 있다. (정확히는 BSD계열이란 표현이 옳지만, 위키피디아 설명을 기준으로 구분할 경우를 말한다.) 터미널에서 man ls로 매뉴얼을 확인해 보면 macOS는 “BSD General Commands Manual"이라고 표현되어 있는 것을 볼 수 있다.

해결법

내가 생각하는 해결법은 2가지다.

  1. macOS에서 GNU ls를 사용한다.
  2. macOS의 BSD ls를 GNU ls와 비슷하게 설정한다.

나는 1번 해결법이 더 좋은 해결법이라 생각한다. 일단 리눅스에서 사용하던 환경을 일관되게 사용하려는 것이 목적이기도 했고, GNU ls는 파일 확장자에 따른 하이라이트도 제공하기 때문이다.

GNU ls 사용하기

먼저 GNU coreutils를 설치한다. ls는 개별적으로 설치하는 것이 아닌, GNU core utilities의 일부다.

homebrew가 설치되었다면 brew install coreutils 명령으로 간단하게 설치할 수 있다.

GNU coreutils를 통해 설치된 명령어는 앞에 g가 붙는다. 즉, GNU coreutils의 lsgls로 호출해야 한다. ls 명령을 호출해도 gls가 호출될 수 있도록, 색상 옵션을 기본으로 넣게 설정하려면 쉘 rc파일에 아래 내용을 추가하자. (아마 최근 macOS는 zsh를 채용했기 때문에 .zshrc를 수정해야 할 것이다.)

if [[ -x "$(command -v gls)" ]]; then
    alias ls='gls --color=auto'
fi

alias를 활용하여 기존 BSD 버전 ls 대신 gls를, --color=auto 옵션까지 기본으로 추가해서 실행하도록 설정해주자.

여기까지 수행하면 일반파일, 실행파일, 디렉토리, 심볼릭 링크까지는 같은 색상으로 출력하지만, 파일 확장자별 구분이나, 깨진 심볼릭 링크까지는 확실히 구분해 주지 못한다.

색상은 통일되었으나, 확장자에 대한 구분을 하지 못 하고 있다.

현재 상태는 GNU ls의 기본 색 구분만 사용하고 있기 때문이다. 이전 원인 분석과정에서 설명했듯 GNU ls의 색상 관리에는 dircolors를 사용한다. GNU coreutils를 설치하면 ls 뿐만 아니라 dircolors도 같이 설치된다. dircolors의 설정을 반영하도록 rc파일을 수정해주자.

if [[ -x "$(command -v gls)" ]]; then
    alias ls='gls --color=auto'
    if [[ -x "$(command -v gdircolors)" ]]; then
        eval "$(gdircolors -b)"
    fi
fi

색상도 통일되고, 확장자에 대한 구분까지 지원된다. 기존 리눅스와 동일한 결과로 출력된다.

BSD ls를 GNU ls와 비슷하게 설정하기

설정 방법은 GNU ls 설치에 비해 매우 간단하다.

환경 변수 중 LSCOLORSExGxFxdxCxdadahbadacec로 설정하면 된다.

해당 값은 기존 GNU ls와 최대한 비슷하도록 값을 설정한 것이다.

rc파일에서 자동으로 설정되게 하려면 아래와 같이 수정한다.

export LSCOLORS="ExGxFxdxCxdadahbadacec"

GNU ls와 비슷한 색으로 출력하고 있지만, 확장자 구분이나, 심볼릭 링크의 유효성 여부는 확인할 수 없다.

이전에 언급했듯 GNU ls에 비해 기능이 부족하기 때문에 다양하게 색 구분은 할 수 없지만, 따로 프로그램 설치 없이 리눅스에서 사용하던 색 배치를 최대한 일관되게 볼 수 있다.

내부 원리

ls가 두 종류로 나뉘는 것을 설명하면서 각각 dircolorstermcap의 데이터베이스를 사용한다는 식의 설명이 있었다. 결과적으로 두 ls 모두 shell 환경변수에서 값을 읽어온다. GNU ls는 LS_COLORS 환경변수를 참조하며, BSD ls는 LSCOLORS 환경변수를 참조한다. (중간에 _ 존재 여부로 차이난다.)

LS_COLORS (GNU ls의 환경 변수)

dircolors의 설정이 반영되도록 eval $(dircolors -b)를 rc파일에 삽입했었다. 그렇다면 dircolors -b는 무슨 일을 하는지 확인해 보자.

$ dircolors --help
Usage: dircolors [OPTION]... [FILE]
Output commands to set the LS_COLORS environment variable.

Determine format of output:
  -b, --sh, --bourne-shell    output Bourne shell code to set LS_COLORS
  -c, --csh, --c-shell        output C shell code to set LS_COLORS
  -p, --print-database        output defaults
      --help     display this help and exit
      --version  output version information and exit

If FILE is specified, read it to determine which colors to use for which
file types and extensions.  Otherwise, a precompiled database is used.
For details on the format of these files, run 'dircolors --print-database'.

GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Full documentation at: <https://www.gnu.org/software/coreutils/dircolors>
or available locally via: info '(coreutils) dircolors invocation'

$ dircolors -b
LS_COLORS='rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:';
export LS_COLORS

설명에 나와있듯 dircolors -bbourne shell 호환 문법으로 LS_COLORS의 값을 환경변수로 설정한다. 현재 LS_COLORS의 값 부분이 길기 때문에 의미를 기준으로 좀 더 쪼개서 다시 보도록 하자.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
LS_COLORS="rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01"
LS_COLORS="$LS_COLORS:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32"
LS_COLORS="$LS_COLORS:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31"
LS_COLORS="$LS_COLORS:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31"
LS_COLORS="$LS_COLORS:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31"
LS_COLORS="$LS_COLORS:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31"
LS_COLORS="$LS_COLORS:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31"
LS_COLORS="$LS_COLORS:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31"
LS_COLORS="$LS_COLORS:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31"
LS_COLORS="$LS_COLORS:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35"
LS_COLORS="$LS_COLORS:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35"
LS_COLORS="$LS_COLORS:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35"
LS_COLORS="$LS_COLORS:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35"
LS_COLORS="$LS_COLORS:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35"
LS_COLORS="$LS_COLORS:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35"
LS_COLORS="$LS_COLORS:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35"
LS_COLORS="$LS_COLORS:*.ogv=01;35:*.ogx=01;35"
LS_COLORS="$LS_COLORS:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36"
LS_COLORS="$LS_COLORS:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36"
LS_COLORS="$LS_COLORS:*.spx=00;36:*.xspf=00;36:"

보면 하나의 설정 당 :로 분리하여 설명하고 있으며, 여러 색 설정을 조합하여 쓰기 위해 ;를 사용하고 있음을 확인할 수 있다.

12번 라인은 파일의 타입에 따라 색 설정을 하도록 하고 있다.
3
9번 라인은 주로 압축 파일 확장자들을 같은 색으로 출력하도록 설정하고 있다. (tar등의 archive 파일이나, deb, rpm, jar 같이 내부적으로는 archive 압축 파일로 구성된 패키지 파일 등)
1012번 라인은 주로 이미지 파일 확장자들을 같은 색으로 출력하도록 설정하고 있다.
13
17번 라인은 주로 동영상 파일 확장자들을 같은 색으로 출력하도록 설정하고 있다.
18~20번 라인은 주로 오디오 파일 확장자들을 같은 색으로 출력하도록 설정하고 있다.

여러 확장자들을 특성에 따라 같은 색으로 표현했으니, 파일 타입은 각각 어떤 방식으로 구분하는지 확인해보자.

코드설명
rs리셋 (기본 색 설정)
di디렉토리
ln심볼릭 링크
mh멀티 하드 링크
pi파이프 파일 (named pipe)
so소켓 파일
do도어 파일 (IPC에서 사용됨)
bd블록 장치 파일
cd글자 장치 파일
or잘못된 링크(없는 파일)를 가리키는 심볼릭 링크
mi심볼릭 링크가 가리키는 잘못된 링크의 파일 (ls -l에서 출력됨)
suSetUID 파일
sgSetGID 파일
ca호환성
twother write가 가능하고 sticky bit가 설정된 디렉토리
owother write가 가능하고 sticky bit는 설정되지 않은 디렉토리
stother write가 불가능하고 sticky bit만 설정된 디렉토리
ex실행 파일

여기서 ca, 호환성 부분은 리눅스 시스템의 capabilities를 의미하는 것 같은데, 이 부분은 완전히 이해하지 못했다.

색상 코드 값은 ISO 6429 색상 코드를 사용하며, 해당 값에 대한 정보는 아래 표와 같다.

글씨배경색상글씨배경색상
3040검정(Black)90100회색(밝은 검정)
3141빨강(Red)91101주황(밝은 빨강)
3242초록(Green)92102연두(밝은 초록)
3343노랑(Yellow)93103노랑(밝은 노랑)
3444파랑(Blue)94104파랑(밝은 파랑)
3545보라(Magenta)95105핑크(밝은 보라)
3646하늘(Cyan)96106하늘(밝은 하늘)
3747하양(White)97107하양(밝은 하양)

색상 외, 글씨에 다른 효과를 주는 코드는 아래 표와 같다.

코드효과
00기본 색상
01굵은 글씨
04밑줄 친
05깜빡이는

참고로 05, 깜빡이는 속성은 직접 실험해봤는데 깜빡이지 않았다.

이제 위의 내용을 바탕으로 LS_COLORS의 값 일부를 해석해보자

설정 값해석
di=01;34디렉토리는 굵은 파란색 글씨로 표현
ln=01;36심볼릭 링크는 굵은 보라색 글씨로 표현
pi=40;33파이프 파일은 검은색 배경에 노란색 글씨로 표현
bd=40;33;01블록 장치 파일은 검은색 배경에 굵은 노란색 글씨로 표현

본 문서에서 설명되지 않은 부분은 공식 man page여기를 참고하자.

LSCOLORS (BSD ls의 환경 변수)

LSCOLORS는 2개 글자씩 해석하여, 아래 순서대로 색상을 결정한다.

  1. 디렉토리의 글씨, 배경 색
  2. 심볼릭 링크의 글씨, 배경 색
  3. 소켓 파일의 글씨, 배경 색
  4. 파이프 파일의 글씨, 배경 색
  5. 실행 파일의 글씨, 배경 색
  6. 블록 장치 파일의 글씨, 배경 색
  7. 글자 장치 파일의 글씨, 배경 색
  8. SetUID 실행 파일의 글씨, 배경 색
  9. SetGID 실행 파일의 글씨, 배경 색
  10. other write 권한의 sticky bit가 없는 디렉토리의 글씨, 배경 색
  11. other write 권한의 sticky bit가 있는 디렉토리의 글씨, 배경 색

색상의 구분 코드는 아래와 같다.

코드색상코드색상
a검은색(black)A굵은 검은색
b빨간색(red)B굵은 빨간색
c초록색(green)C굵은 초록색
d갈색(brown)D굵은 갈색
e파란색(blue)E굵은 파란색
f보라색(magenta)F굵은 보라색
g하늘색(cyan)G굵은 하늘색
h회색(grey)H굵은 회색
x기본색(흰색)X굵은 기본색

위의 GNU ls 대비 노란색, 오렌지 등이 없으며, 색 반전, 밑줄 등의 효과가 없는 것을 알 수 있다.

그럼 이전 해결법에서 설정한 LSCOLORS의 값 ExGxFxdxCxdadahbadacec을 순서대로 해석해보자.

글씨배경설명
Ex디렉토리는 굵은 파란색 글씨, 배경은 없음
Gx심볼릭 링크는 굵은 하늘색 글씨, 배경은 없음
Fx소켓 파일은 굵은 보라색 글씨, 배경은 없음
dx파이프 파일은 갈색 글씨, 배경은 없음
Cx실행 파일은 굵은 초록색 글씨, 배경은 없음
da블록 장치 파일은 갈색 글씨, 배경은 검은색
da글자 장치 파일은 갈색 글씨, 배경은 검은색
hbSetUID 실행 파일은 회색 글씨, 배경은 빨간색
adSetGID 실행 파일은 검은색 글씨, 배경은 갈색
acother write, sticky bit가 있는 디렉토리는 검은색 글씨, 배경은 초록색
ecother write, sticky bit가 없는 디렉토리는 파란색 글씨, 배경은 초록색

만약 LSCOLORS의 값을 원하는 대로 수정하고 싶다면, 여기에서 직접 만들 수 있다. (GNU ls의 LS_COLORS도 출력할 수 있지만, 기존 dircolors 대비 색상의 종류가 부족하므로 추천하지 않는다.)

GNU ls 내부 구조

분석한 코드 버전은 여기를 참고하자.

596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
enum indicator_no
  {
    C_LEFT, C_RIGHT, C_END, C_RESET, C_NORM, C_FILE, C_DIR, C_LINK,
    C_FIFO, C_SOCK,
    C_BLK, C_CHR, C_MISSING, C_ORPHAN, C_EXEC, C_DOOR, C_SETUID, C_SETGID,
    C_STICKY, C_OTHER_WRITABLE, C_STICKY_OTHER_WRITABLE, C_CAP, C_MULTIHARDLINK,
    C_CLR_TO_EOL
  };

static char const *const indicator_name[]=
  {
    "lc", "rc", "ec", "rs", "no", "fi", "di", "ln", "pi", "so",
    "bd", "cd", "mi", "or", "ex", "do", "su", "sg", "st",
    "ow", "tw", "ca", "mh", "cl", NULL
  };
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
static struct bin_str color_indicator[] =
  {
    { LEN_STR_PAIR ("\033[") },		/* lc: Left of color sequence */
    { LEN_STR_PAIR ("m") },		/* rc: Right of color sequence */
    { 0, NULL },			/* ec: End color (replaces lc+rs+rc) */
    { LEN_STR_PAIR ("0") },		/* rs: Reset to ordinary colors */
    { 0, NULL },			/* no: Normal */
    { 0, NULL },			/* fi: File: default */
    { LEN_STR_PAIR ("01;34") },		/* di: Directory: bright blue */
    { LEN_STR_PAIR ("01;36") },		/* ln: Symlink: bright cyan */
    { LEN_STR_PAIR ("33") },		/* pi: Pipe: yellow/brown */
    { LEN_STR_PAIR ("01;35") },		/* so: Socket: bright magenta */
    { LEN_STR_PAIR ("01;33") },		/* bd: Block device: bright yellow */
    { LEN_STR_PAIR ("01;33") },		/* cd: Char device: bright yellow */
    { 0, NULL },			/* mi: Missing file: undefined */
    { 0, NULL },			/* or: Orphaned symlink: undefined */
    { LEN_STR_PAIR ("01;32") },		/* ex: Executable: bright green */
    { LEN_STR_PAIR ("01;35") },		/* do: Door: bright magenta */
    { LEN_STR_PAIR ("37;41") },		/* su: setuid: white on red */
    { LEN_STR_PAIR ("30;43") },		/* sg: setgid: black on yellow */
    { LEN_STR_PAIR ("37;44") },		/* st: sticky: black on blue */
    { LEN_STR_PAIR ("34;42") },		/* ow: other-writable: blue on green */
    { LEN_STR_PAIR ("30;42") },		/* tw: ow w/ sticky: black on green */
    { LEN_STR_PAIR ("30;41") },		/* ca: black on red */
    { 0, NULL },			/* mh: disabled by default */
    { LEN_STR_PAIR ("\033[K") },	/* cl: clear to end of line */
  };
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
static void
parse_ls_color (void)
{
  char const *p;		/* Pointer to character being parsed */
  char *buf;			/* color_buf buffer pointer */
  int ind_no;			/* Indicator number */
  char label[3];		/* Indicator label */
  struct color_ext_type *ext;	/* Extension we are working on */

  if ((p = getenv ("LS_COLORS")) == NULL || *p == '\0')
    {
      /* LS_COLORS takes precedence, but if that's not set then
         honor the COLORTERM and TERM env variables so that
         we only go with the internal ANSI color codes if the
         former is non empty or the latter is set to a known value.  */
      char const *colorterm = getenv ("COLORTERM");
      if (! (colorterm && *colorterm) && ! known_term_type ())
        print_with_color = false;
      return;
    }

  ext = NULL;
  strcpy (label, "??");

  /* This is an overly conservative estimate, but any possible
     LS_COLORS string will *not* generate a color_buf longer than
     itself, so it is a safe way of allocating a buffer in
     advance.  */
  buf = color_buf = xstrdup (p);

  enum parse_state state = PS_START;
  while (true)
    {
      switch (state)
        {
        case PS_START:		/* First label character */
          switch (*p)
            {
            case ':':
              ++p;
              break;

            case '*':
              /* Allocate new extension block and add to head of
                 linked list (this way a later definition will
                 override an earlier one, which can be useful for
                 having terminal-specific defs override global).  */

              ext = xmalloc (sizeof *ext);
              ext->next = color_ext_list;
              color_ext_list = ext;

              ++p;
              ext->ext.string = buf;

              state = (get_funky_string (&buf, &p, true, &ext->ext.len)
                       ? PS_4 : PS_FAIL);
              break;

            case '\0':
              state = PS_DONE;	/* Done! */
              goto done;

            default:	/* Assume it is file type label */
              label[0] = *(p++);
              state = PS_2;
              break;
            }
          break;

        case PS_2:		/* Second label character */
          if (*p)
            {
              label[1] = *(p++);
              state = PS_3;
            }
          else
            state = PS_FAIL;	/* Error */
          break;

        case PS_3:		/* Equal sign after indicator label */
          state = PS_FAIL;	/* Assume failure...  */
          if (*(p++) == '=')/* It *should* be...  */
            {
              for (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no)
                {
                  if (STREQ (label, indicator_name[ind_no]))
                    {
                      color_indicator[ind_no].string = buf;
                      state = (get_funky_string (&buf, &p, false,
                                                 &color_indicator[ind_no].len)
                               ? PS_START : PS_FAIL);
                      break;
                    }
                }
              if (state == PS_FAIL)
                error (0, 0, _("unrecognized prefix: %s"), quote (label));
            }
          break;

        case PS_4:		/* Equal sign after *.ext */
          if (*(p++) == '=')
            {
              ext->seq.string = buf;
              state = (get_funky_string (&buf, &p, false, &ext->seq.len)
                       ? PS_START : PS_FAIL);
            }
          else
            state = PS_FAIL;
          break;

        case PS_FAIL:
          goto done;

        default:
          abort ();
        }
    }
 done:

  if (state == PS_FAIL)
    {
      struct color_ext_type *e;
      struct color_ext_type *e2;

      error (0, 0,
             _("unparsable value for LS_COLORS environment variable"));
      free (color_buf);
      for (e = color_ext_list; e != NULL; /* empty */)
        {
          e2 = e;
          e = e->next;
          free (e2);
        }
      print_with_color = false;
    }

  if (color_indicator[C_LINK].len == 6
      && !STRNCMP_LIT (color_indicator[C_LINK].string, "target"))
    color_symlink_as_referent = true;
}

BSD ls 내부 구조

분석한 코드 버전은 여기를 참고하자.

511
512
513
514
515
516
517
518
519
520
521
522
523
#ifdef COLORLS
	if (f_color) {
		/*
		 * We can't put tabs and color sequences together:
		 * column number will be incremented incorrectly
		 * for "stty oxtabs" mode.
		 */
		f_notabs = 1;
		(void)signal(SIGINT, colorquit);
		(void)signal(SIGQUIT, colorquit);
		parsecolors(getenv("LSCOLORS"));
	}
#endif
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
#ifdef COLORLS
/* Most of these are taken from <sys/stat.h> */
typedef enum Colors {
	C_DIR,			/* directory */
	C_LNK,			/* symbolic link */
	C_SOCK,			/* socket */
	C_FIFO,			/* pipe */
	C_EXEC,			/* executable */
	C_BLK,			/* block special */
	C_CHR,			/* character special */
	C_SUID,			/* setuid executable */
	C_SGID,			/* setgid executable */
	C_WSDIR,		/* directory writeble to others, with sticky
				 * bit */
	C_WDIR,			/* directory writeble to others, without
				 * sticky bit */
	C_NUMCOLORS		/* just a place-holder */
} Colors;

static const char *defcolors = "exfxcxdxbxegedabagacad";
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
void
parsecolors(const char *cs)
{
	int i;
	int j;
	size_t len;
	char c[2];
	short legacy_warn = 0;

	if (cs == NULL)
		cs = "";	/* LSCOLORS not set */
	len = strlen(cs);
	for (i = 0; i < (int)C_NUMCOLORS; i++) {
		colors[i].bold = 0;

		if (len <= 2 * (size_t)i) {
			c[0] = defcolors[2 * i];
			c[1] = defcolors[2 * i + 1];
		} else {
			c[0] = cs[2 * i];
			c[1] = cs[2 * i + 1];
		}
		for (j = 0; j < 2; j++) {
			/* Legacy colours used 0-7 */
			if (c[j] >= '0' && c[j] <= '7') {
				colors[i].num[j] = c[j] - '0';
				if (!legacy_warn) {
					warnx("LSCOLORS should use "
					    "characters a-h instead of 0-9 ("
					    "see the manual page)");
				}
				legacy_warn = 1;
			} else if (c[j] >= 'a' && c[j] <= 'h')
				colors[i].num[j] = c[j] - 'a';
			else if (c[j] >= 'A' && c[j] <= 'H') {
				colors[i].num[j] = c[j] - 'A';
				colors[i].bold = 1;
			} else if (tolower((unsigned char)c[j]) == 'x')
				colors[i].num[j] = -1;
			else {
				warnx("invalid character '%c' in LSCOLORS"
				    " env var", c[j]);
				colors[i].num[j] = -1;
			}
		}
	}
}

JaeSang Yoo
글쓴이
JaeSang Yoo
The Programmer

목차