|
| 1 | +# 3장. 셸과 스크립팅 |
| 2 | + |
| 3 | +CLI 관점에서 리눅스와 상호작용하는 방법 |
| 4 | + |
| 5 | +1. 수동으로 명령 입력 |
| 6 | +2. 스크립팅으로 일련의 명령 자동화 |
| 7 | + |
| 8 | +## 기본 개요 |
| 9 | + |
| 10 | +### 터미널 |
| 11 | + |
| 12 | +> 텍스트로 된 UI를 제공하는 프로그램 |
| 13 | +
|
| 14 | += 터미널, 터미널 에뮬레이터, 소프트 터미널 |
| 15 | + |
| 16 | +- 커서, 화면 처리, 색상 등의 지원을 위해 https://en.wikipedia.org/wiki/ANSI_escape_code 지원 |
| 17 | + | 코드 | 의미 | 대표 용도 | |
| 18 | + | --------------- | -------------- | --------------- | |
| 19 | + | `\n` | newline | 출력 | |
| 20 | + | `\t` | tab | 출력 정렬 | |
| 21 | + | `\\` | backslash | escaping | |
| 22 | + | `\r` | 줄 맨 앞으로 | progress | |
| 23 | + | `\033` / `\x1b` | ESC | ANSI 시작 | |
| 24 | + | `\0330m` | reset | **색상 초기화** | |
| 25 | + | `\033[1m` | bold | 강조 | |
| 26 | + | `\033[31m` | red | error | |
| 27 | + | `\033[32m` | green | success | |
| 28 | + | `\033[33m` | yellow | warning | |
| 29 | + | `\033[34m` | blue | info | |
| 30 | + | `\033[nA/B/C/D` | cursor 이동 | CLI/TUI | |
| 31 | + | `\033[K` | 줄 끝까지 삭제 | progress 갱신 | |
| 32 | + | `\033[2K` | 현재 줄 삭제 | CLI 갱신 | |
| 33 | + | `\033[2J` | 화면 삭제 | TUI | |
| 34 | + ```bash |
| 35 | + Shell escape |
| 36 | + │ |
| 37 | + ├─ 문자 제어 |
| 38 | + │ ├─ \n 줄바꿈 |
| 39 | + │ ├─ \t 탭 |
| 40 | + │ ├─ \r 줄 처음 |
| 41 | + │ └─ \\ backslash |
| 42 | + │ |
| 43 | + └─ Terminal 제어 |
| 44 | + └─ \033[... / \x1b[... |
| 45 | + ├─ ...m 색/스타일 |
| 46 | + ├─ ...A-D 커서 이동 |
| 47 | + ├─ ...K 줄 삭제 |
| 48 | + └─ ...J 화면 삭제 |
| 49 | + ``` |
| 50 | +- `TERM` 환경변수는 사용 중인 터미널 에뮬레이터 값을 가지며, `infocmp`를 통해 구성 가능 |
| 51 | +- GPU 활용 터미널 - Alacritty, kitty, warp |
| 52 | +
|
| 53 | +### 셸 |
| 54 | +
|
| 55 | +> 터미널 내부에서 실행되는 명령 인터프리터 역할의 프로그램 |
| 56 | +
|
| 57 | +- 스트림을 통해 입출력 처리, 변수 지원, 빌트인 명령 존재 |
| 58 | +- 수동 / 스크립팅 방식 모두 지원 |
| 59 | +- `sh` 로 정의 (fyi. [POSIX sh - shell의 표준 문법을 의미) |
| 60 | + - 최근에는 bash 셸이 주로 쓰임 |
| 61 | +
|
| 62 | + ```bash |
| 63 | + Unix Shell |
| 64 | +
|
| 65 | + POSIX(Standard) |
| 66 | + │ |
| 67 | + ┌──────────┼───────────┐ |
| 68 | + │ │ │ |
| 69 | + bash dash ksh |
| 70 | + │ │ |
| 71 | + /bin/bash /bin/sh |
| 72 | + ``` |
| 73 | +
|
| 74 | + - ksh(korn shell), csh(C shell)과 같은 변형도 있음 |
| 75 | + - 어떤 sh를 사용 중인지 확인하려면? |
| 76 | + ```bash |
| 77 | + $ echo $0 |
| 78 | + -bash |
| 79 | + $ echo $SHELL |
| 80 | + /bin/bash |
| 81 | + $ file -h /bin/sh |
| 82 | + /bin/sh: symbolic link to bash |
| 83 | + ``` |
| 84 | +
|
| 85 | +**[기본 기능]** |
| 86 | +
|
| 87 | +- 스트림 |
| 88 | + - 셸에 입력하는 명령은 키보드에서 입력(stdin)을 가져오고, 출력(stdout)을 화면에 전달한다. \***_default 동작_** |
| 89 | + - 스트림을 redirect하여 위 동작을 제어할 수 있다 |
| 90 | + - `$FD>`, `<$FD` |
| 91 | +  |
| 92 | + | FD | 이름 | 기본 연결 | |
| 93 | + | --- | ------ | --------- | |
| 94 | + | `0` | stdin | 키보드 | |
| 95 | + | `1` | stdout | 터미널 | |
| 96 | + | `2` | stderr | 터미널 | |
| 97 | + - `&>` : stdout, stderr 모두 redirect |
| 98 | + - `/dev/null` : 스트림 버리기 |
| 99 | + - `&` : 백그라운드에서 실행 (명령 마지막에 배치) |
| 100 | + - `\` : 긴 명령의 가독성 개선을 위한 개행 지원 |
| 101 | + - `|` : 한 프로세스의 stdout 값을 다음 프로세스의 stdin과 연결해 임시 저장 없이 즉시 전달 |
| 102 | + - 문법 정리 |
| 103 | + | 문법 | 의미 | 대상 FD | 예시 | |
| 104 | + | ---------------- | ------------------------ | ------- | ----------------------- | |
| 105 | + | `>` | stdout 덮어쓰기 | 1 | `ls > out.txt` | |
| 106 | + | `>>` | stdout 이어쓰기 | 1 | `echo hi >> log.txt` | |
| 107 | + | `<` | stdin 입력 | 0 | `sort < data.txt` | |
| 108 | + | `2>` | stderr 덮어쓰기 | 2 | `gcc main.c 2> err.log` | |
| 109 | + | `2>>` | stderr 이어쓰기 | 2 | `cmd 2>> error.log` | |
| 110 | + | `&>` _(Bash)_ | stdout+stderr 모두 | 1,2 | `cmd &> all.log` | |
| 111 | + | `2>&1` | stderr를 stdout으로 합침 | 2→1 | `cmd > log.txt 2>&1` | |
| 112 | + | `1>&2` | stdout을 stderr로 보냄 | 1→2 | `echo error 1>&2` | |
| 113 | + | `/dev/null` | 출력 버리기 | - | `cmd > /dev/null` | |
| 114 | + | `/dev/null 2>&1` | 모든 출력 버리기 | - | `cmd > /dev/null 2>&1` | |
| 115 | +
|
| 116 | +- 변수 |
| 117 | + - 사용 케이스 |
| 118 | + 1. 리눅스가 노출하는 구성 항목을 처리하려는 경우 - 변수 read/write에 대한 일종의 인터페이스 |
| 119 | + - e.g. 셸이 $PATH 변수에 저장된 실행 파일을 찾는 위치 |
| 120 | + 2. 스크립트에서 사용자에게 값을 대화 형식으로 질문하려는 경우 |
| 121 | + 3. 긴 값을 한 번 정의해 입력을 줄이려는 경우 |
| 122 | + - 유형 |
| 123 | + - 환경변수 - 셸 전체 설정으로, `env`로 목록 나열 |
| 124 | + - 셸 변수 - 현재 실행 상황에 유효 - bash 관련 변수 목록 - https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html |
| 125 | +  |
| 126 | + `echo $XXX` 명령으로 해당 값 조회 가능 |
| 127 | + - `_=` : 환경변수로 내보내지 않았음을 의미 |
| 128 | + - 실행결과 |
| 129 | +
|
| 130 | + ```bash |
| 131 | + $ echo $EDITOR |
| 132 | +
|
| 133 | + $ echo $HOME |
| 134 | + /home/deploy |
| 135 | + $ echo $HOSTNAME |
| 136 | + techmem-cube-server01 |
| 137 | + $ echo $IFS |
| 138 | +
|
| 139 | + $ echo $PATH |
| 140 | + /home/deploy/.local/bin:/home/deploy/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin |
| 141 | + $ |
| 142 | + ^[[A$ echo $PS1 |
| 143 | + [\u@\h \W]\$ |
| 144 | + $ echo $PWD |
| 145 | + /home/deploy |
| 146 | + $ echo $OLDPWD |
| 147 | +
|
| 148 | + $ echo $RANDOM |
| 149 | + 26431 |
| 150 | + $ echo $SHELL |
| 151 | + /bin/bash |
| 152 | + $ echo $TERM |
| 153 | + xterm-256color |
| 154 | + $ echo $UID |
| 155 | + 1100 |
| 156 | + $ echo $USER |
| 157 | + deploy |
| 158 | + $ echo $_ |
| 159 | + command_prexec |
| 160 | + $ echo $? |
| 161 | + 0 |
| 162 | + $ echo $$ |
| 163 | + 276033 |
| 164 | + $ echo $0 |
| 165 | + -bash |
| 166 | + ``` |
| 167 | +
|
| 168 | +- 종료 상태 |
| 169 | + - 정상 종료(happy path) **`0`** |
| 170 | + - 비정상 종료 **`1`~`255`** |
| 171 | + - `echo $?` : 종료 상태 확인 |
| 172 | + - `$PIPESTATUS` 를 사용해 파이프 사용 시에 마지막 상태값만 사용하게 되는 것을 방지할 수 있다. |
| 173 | +- 내장 명령어 |
| 174 | + - yes, echo, cat, read |
| 175 | + - 프로그램 경로나 셸의 alias, 함수 확인 시 `which`보다 **`command -v`** 사용 권장 |
| 176 | +- 작업 제어 |
| 177 | +  |
| 178 | + - 기본 동작은 명령을 입력하면 해당 명령이 화면과 키보드를 제어하는 foreground 실행이다. |
| 179 | + - `&` 로 프로세스를 백그라운드로 보낼 수 있다. |
| 180 | + - 셸을 닫은 이후에도 프로세스를 지속하려면 `nohup` 을 앞에 추가한다. |
| 181 | + - 이미 실행된 이후라도 `disown`으로 무중단시킬 수 있다. |
| 182 | + - `kill` 로 다양한 수준의 강제성을 부여한 프로세스 제거가 가능하다. |
| 183 | +- **작업 제어보다는 터미널 멀티플렉서 사용을 추천** |
| 184 | +
|
| 185 | +## 모던 리눅스 명령어 |
| 186 | +
|
| 187 | +> 자주 사용하는 `cd`, `ls`, `find`, `cat`, `less`와 같은 명령을 효율적으로 사용하자! |
| 188 | +
|
| 189 | +1. 드롭인 교체(drop-in replacement) |
| 190 | +2. 기능 확장 |
| 191 | +
|
| 192 | +- 특징 |
| 193 | + - 정상적인 기본값 제공 |
| 194 | + - 일반적으로 이해하기 쉬운 풍부한 출력값 |
| 195 | + - 동일한 작업에 대해 축소된 입력량 |
| 196 | +- `ls` → `exa` : 디렉터리 내용 나열 |
| 197 | + - https://github.com/ogham/exa |
| 198 | + - clear 기능을 Ctrl+L 키로 대체 |
| 199 | +- `cat` → `bat` : 파일 내용 조회 |
| 200 | + - https://github.com/sharkdp/bat |
| 201 | + - 구문 강조 표시, 인쇄 불가능 문자 표시, 깃 지원, 자체 페이저 기능 내장 |
| 202 | +- `find` + `grep` → `rg` : 파일에서 콘텐츠 검색 |
| 203 | + - https://github.com/BurntSushi/ripgrep |
| 204 | + !image.png |
| 205 | +- `awk`/`sed` → `jq` : JSON 데이터 파싱 |
| 206 | + - https://jqlang.org/ |
| 207 | +
|
| 208 | +### 일반 작업 |
| 209 | +
|
| 210 | +- 자주 사용하는 명령어 단축하기 |
| 211 | + - bash shell → **_alias_** / fish shell → **_abbreviations_** |
| 212 | +- 행 탐색과 조작 |
| 213 | + - Shell 탐색 및 편집 단축키 |
| 214 | + | 동작 | 단축키 | 알파벳 의미(Mnemonic) | 비고 | |
| 215 | + | --------------------- | -------- | ------------------------- | ------------------------ | |
| 216 | + | 행의 시작으로 이동 ⭐ | `Ctrl+A` | **A = Ahead / Beginning** | Home과 동일 | |
| 217 | + | 행의 끝으로 이동 ⭐ | `Ctrl+E` | **E = End** | End와 동일 | |
| 218 | + | 문자 한 칸 앞으로 ⭐ | `Ctrl+F` | **F = Forward** | → | |
| 219 | + | 문자 한 칸 뒤로 ⭐ | `Ctrl+B` | **B = Backward** | ← | |
| 220 | + | 단어 앞으로 | `Alt+F` | **Forward Word** | 왼쪽 Alt 권장 | |
| 221 | + | 단어 뒤로 | `Alt+B` | **Backward Word** | | |
| 222 | + | 현재 문자 삭제 | `Ctrl+D` | **D = Delete** | EOF 역할도 함 | |
| 223 | + | 왼쪽 문자 삭제 | `Ctrl+H` | **H = Backspace** | ASCII Backspace(0x08) | |
| 224 | + | 왼쪽 단어 삭제 ⭐ | `Ctrl+W` | **W = Word** | 단어 삭제 | |
| 225 | + | 오른쪽 모두 삭제 ⭐ | `Ctrl+K` | **K = Kill** | Kill to End | |
| 226 | + | 왼쪽 모두 삭제 ⭐ | `Ctrl+U` | **Unix Kill Line** | Kill to Beginning | |
| 227 | + | 화면 지우기 ⭐ | `Ctrl+L` | **L = (c)Lear** | `clear`와 동일 | |
| 228 | + | 명령 취소 ⭐ | `Ctrl+C` | **C = Cancel** | SIGINT 전송 | |
| 229 | + | 실행 취소 | `Ctrl+\` | **Quit** | SIGQUIT (Core Dump 가능) | |
| 230 | + | 히스토리 검색 ⭐ | `Ctrl+R` | **R = Reverse Search** | 매우 자주 사용 | |
| 231 | + | 검색 종료 | `Ctrl+G` | **G = Go Back / Abort** | 일부 Shell | |
| 232 | +- 긴 파일 보기 |
| 233 | + - `head`, `tail` |
| 234 | + - `-f` : 실시간 출력 |
| 235 | +
|
| 236 | +## 인간 친화적인 셸 |
| 237 | +
|
| 238 | +### fish shell |
| 239 | +
|
| 240 | +https://fishshell.com/ |
| 241 | +
|
| 242 | +- 명시적인 이력 관리X |
| 243 | +- 자동완성&시각적 힌트 기능을 대다수 명령에 사용 가능 |
| 244 | +
|
| 245 | +
|
| 246 | +### zsh |
| 247 | +
|
| 248 | +https://zsh.sourceforge.io/Doc/ |
| 249 | +
|
| 250 | +- 5개의 시작 파일 사용 (기본 $ZDOTDIR → $HOME) |
| 251 | +- https://github.com/unixorn/awesome-zsh-plugins |
| 252 | +
|
| 253 | +### 최신 셸 목록 |
| 254 | +
|
| 255 | +- **Oil Shell** → Bash를 현대적으로 개선한 스크립팅 Shell |
| 256 | +- **Murex** → 타입이 있는 파이프라인을 지원하는 프로그래밍 지향 Shell |
| 257 | +- **Nushell** → JSON/테이블 등 구조화된 데이터를 다루기 좋은 차세대 Shell |
| 258 | +- **PowerShell** → Windows 및 인프라 자동화에 사실상 표준인 객체 기반 Shell |
| 259 | +
|
| 260 | +## 터미널 멀티플렉서 |
| 261 | +
|
| 262 | +> 다중화(multiflexing)를 기반으로 터미널 사용 개선하기 |
| 263 | +
|
| 264 | +- N개의 창을 띄워두고 동시에 상호 의존적인 작업을 수행하는 경우 |
| 265 | +- 논리적으로 하나의 단위에 속하는 작업을 그룹화한 것이 **`세션`** |
| 266 | +- 터미널 멀티플렉서는 터미널 오버레이, 즉 터미널 I/O 다중화를 제공하여 여러 창을 띄워 작업하고, 윈도우/경로를 유지하며 세션 탐색이 가능하도록 지원한다 |
| 267 | + - screen |
| 268 | + - tmux |
| 269 | + | 단위 | 의미 | 관계 / 특징 | 비유 | |
| 270 | + | ------------------ | ------------------------------------- | -------------------------------------------------------- | ------------------------------------ | |
| 271 | + | **세션 (Session)** | 특정 작업을 위한 **논리적 작업 환경** | 가장 큰 단위. 윈도·영역 등 모든 단위가 세션에 속함 | 프로젝트 X 작업 공간 / 브라우저 전체 | |
| 272 | + | **윈도 (Window)** | 세션 내부의 **작업 단위** | 선택 사항. 보통 세션당 하나의 윈도 사용 | 브라우저의 **탭** | |
| 273 | + | **영역 (Pane)** | **실행 가능한 단일 셸 인스턴스** | 윈도의 일부. 가로/세로 분할 가능, 확대·축소 및 닫기 가능 | 실제 **터미널 화면** | |
| 274 | + ```rust |
| 275 | + Session |
| 276 | + └── Window |
| 277 | + ├── Pane → Shell |
| 278 | + ├── Pane → Shell |
| 279 | + └── Pane → Shell |
| 280 | + ``` |
| 281 | + - tmuxinator |
| 282 | + - tmux 세션을 관리할 수 있는 메타 도구 |
| 283 | + - Byobu |
| 284 | + - screen 또는 tmux의 래퍼(wrapper) |
| 285 | + - Ubuntu·Debian 기반 리눅스 배포판에서 사용하기 좋은 선택 |
| 286 | + - Zellij |
| 287 | + - "차세대 터미널 작업 공간"을 지향하는 터미널 멀티플렉서 |
| 288 | + - Rust로 작성 |
| 289 | + - 레이아웃 엔진과 강력한 플러그인 시스템 제공 |
| 290 | + - tmux 이상의 기능을 목표로 함 |
| 291 | + - dvtm |
| 292 | + - 터미널에 타일링 윈도우 관리 개념을 도입 |
| 293 | + - 기능은 강력하지만 tmux보다 학습 난이도가 높은 편 |
| 294 | + - 3mux |
| 295 | + - Go 언어로 작성된 간단한 터미널 멀티플렉서 |
| 296 | + - 사용하기 쉽지만 tmux만큼 강력하지는 않음 |
| 297 | + - Alacritty |
| 298 | +
|
| 299 | +## 스크립팅 |
| 300 | +
|
| 301 | +- oils : 수천 행의 코드를 기록하는 shell script |
| 302 | +- 고급 데이터 유형 - 배열 타입 지원 |
| 303 | + ```sql |
| 304 | + os=('Linux','macOS','Windows') |
| 305 | + echo "${os[0]}" # 첫 번째 요소 접근 |
| 306 | + numberofos="${#os[@]}" # 배열의 길이 |
| 307 | + ``` |
| 308 | +- 흐름 제어 - 반복문 (for, while) |
| 309 | + !image.png |
| 310 | +- 함수 정의 → 모듈화되고 재사용 가능한 스크립트 구성 |
| 311 | +- 고급 I/O |
| 312 | + - `read` : 런타임 입력 유도 시 사용하는 stdin의 사용자 입력 읽기 |
| 313 | + - `signal` |
| 314 | + - `trap` |
| 315 | + ```sql |
| 316 | + $ trap -l |
| 317 | + 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP |
| 318 | + 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 |
| 319 | + 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM |
| 320 | + 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP |
| 321 | + 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ |
| 322 | + 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR |
| 323 | + 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 |
| 324 | + 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 |
| 325 | + 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 |
| 326 | + 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 |
| 327 | + 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 |
| 328 | + 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 |
| 329 | + 63) SIGRTMAX-1 64) SIGRTMAX |
| 330 | + ``` |
| 331 | +
|
| 332 | +Bash scripting cheatsheet |
| 333 | +
|
| 334 | +### 이식 가능한 배시 스크립트란? |
| 335 | +
|
| 336 | +> 스크립트가 실행될 다양한 환경을 지원하는 스크립트로, 시스템 종류 및 버전에 따라 동작이 달라질 수 있어 **얼마나 다양한 환경에서 스크립트를 테스트할 수 있는지**가 중요 |
| 337 | +
|
| 338 | +- 단순 텍스트 파일이 실행 가능한 스크립트가 되기까지 |
| 339 | + 1. `#!` 로 작성하는 shebang을 사용해 인터프리터 정의 |
| 340 | + 2. `chmod +x`(안전하게는 `chmod 750`) 로 실행 가능하도록 권한 부여 |
| 341 | +- 스켈레톤 템플릿 |
| 342 | +
|
| 343 | + ```sql |
| 344 | + #! /usr/bin/env bash # 프로그램 로더에게 배시를 사용하 이 스크립트를 해석하도록 지시 |
| 345 | + set -o errexit # 오류 발생 시 스크립트 실행 중지 |
| 346 | + set -o nounset # 설정되지 않은 변수는 오류로 처리하도록 정의 |
| 347 | + set -o pipefail # 파이프 일부 고장 시 전체 고장으로 간주 (조용한 실패르 방지) |
| 348 | +
|
| 349 | + firstargument="${1:-somedefaultvalue}" # 기본값이 있는 명령행 매개변수 예제 |
| 350 | +
|
| 351 | + echo "$firstargument" |
| 352 | + ``` |
| 353 | +
|
| 354 | +- good practice |
| 355 | + 1. 빠르고 요란하게 실패한다 |
| 356 | + 2. 민감한 정보의 하드코딩은 하지 않는다 |
| 357 | + 3. 변수에 정상적인 기본값을 세팅하고, 소스로부터 받은 입력값을 깔끔하게 정리한다 |
| 358 | + 4. 특정 도구나 명령의 사용은 가정하지 않는다 (의존성) |
| 359 | + 5. 스크립트 실패 시 사용자의 실행 지침을 제공한다 |
| 360 | + 6. 메인 블록의 스크립트를 인라인으로 문서화한다 |
| 361 | + 7. 깃과 같은 툴로 스크립트 버전을 관리한다 |
| 362 | + 8. 스크립트를 린트하고 테스트한다 |
| 363 | +
|
| 364 | +### 스크립트 린트와 테스트 |
| 365 | +
|
| 366 | +- ShellCheck 프로그램 : https://blog.davidjeddy.com/2018/11/27/using-shellcheck-to-lint-your-bash-sh-scripts/ (lint) |
| 367 | +- 스크립트 포맷 정의 : sh |
| 368 | +- 스크립트 테스트 : bats |
| 369 | +- 모듈성 향상 : bashing, rerun, rr |
0 commit comments