LabHub

LFCS — 리눅스 재단 시스템 관리자 · 필수 명령과 텍스트 처리 · 이론

셸은 언제 무엇을 대신 해 주는가

LabHub 에서 이어서 보기

한 줄 요약

시험에서 명령이 예상과 다르게 동작하는 이유의 대부분은 명령이 아니라 셸이 명령을 실행하기 전에 한 일 때문이다. 확장 시점, 파일 디스크립터, find 의 표현식 문법 — 이 세 가지가 필수 명령 영역의 뼈대다.

왜 이게 필요했나

rm -f /var/log/*.gz 를 실행했는데 파일이 지워지지 않는다. rm 을 의심하지만 범인은 rm 이 아니다. 와일드카드를 실제 파일 목록으로 바꾸는 주체는 셸이고, 셸은 명령을 부르기 전에 그 일을 끝낸다. 매칭되는 파일이 없으면 셸은 *.gz 라는 글자 그대로를 인자로 넘겨 버리고, rm 은 그런 이름의 파일이 없다고 답한다.

따옴표의 의미도 여기서 갈린다. 큰따옴표는 변수 확장은 살리고 단어 분리와 파일명 확장만 막는다. 작은따옴표는 전부 막는다. 공백이 들어간 경로를 다룰 때 큰따옴표를 빠뜨리면 인자 하나가 두 개로 쪼개진다. 시험에서 경로에 공백이 있는 문제가 나오는 이유가 이것이다.

어떻게 동작하나

리다이렉션은 파일 디스크립터 조작이다. 모든 프로세스는 0(표준 입력), 1(표준 출력), 2(표준 오류)를 갖고 시작한다. > out.txt 는 "1번 디스크립터가 가리키는 곳을 이 파일로 바꿔라" 라는 뜻이고, 2>&1 은 "2번을 지금 1번이 가리키는 곳과 같은 곳으로 복제하라" 라는 뜻이다.

순서가 중요한 이유가 바로 이 '지금' 이다.

| 표기 | 결과 |
| --- | --- |
| cmd > f 2>&1 | 1번을 f 로 바꾼 뒤 2번을 f 로 복제 → 둘 다 파일 |
| cmd 2>&1 > f | 2번을 터미널로 복제한 뒤 1번만 f 로 바꿈 → 오류는 화면 |

find 는 명령이 아니라 표현식 언어다. -name, -size, -mtime, -perm 은 옵션이 아니라 참/거짓을 돌려주는 술어이고, 나열하면 암묵적 AND 로 이어진다. -print-exec 도 술어이며 부수 효과가 있는 술어일 뿐이다. 그래서 -o 로 OR 를 쓸 때 괄호를 빠뜨리면 우선순위가 어긋나 엉뚱한 파일이 잡힌다.

숫자 인자에는 부호 규칙이 붙는다. -mtime 30 은 "정확히 30일 전 구간", -mtime +30 은 "30일보다 더 오래된", -mtime -30 은 "30일 이내" 다. -perm 644 는 정확히 일치, -perm -644 는 그 비트를 모두 포함, -perm /644 는 그중 하나라도 포함이다. 시험에서 가장 많이 깎이는 지점이다.

현장에서 만나는 모습

저자가 정리한 파일 디스크립터 글에는 이런 사례가 나온다. 로그 파일을 실수로 지웠는데, 그 파일을 열고 있던 프로세스가 아직 살아 있었다. 디렉터리 엔트리는 사라졌지만 링크 수가 0 이 되어도 열린 디스크립터가 남아 있으면 커널은 블록을 회수하지 않는다. 그래서 /proc/<PID>/fd/<번호> 를 그대로 복사하는 것만으로 내용을 되살릴 수 있었다. 반대로 같은 성질을 이용해 truncate -s 0 /proc/<PID>/fd/<번호> 로 프로세스를 살린 채 공간만 비울 수도 있는데, 번호를 잘못 짚으면 멀쩡한 파일이 날아간다.

파이프라인에서는 yes | head -1 이 교과서다. head 가 먼저 끝나면 yes 는 읽는 쪽이 없는 파이프에 쓰다가 SIGPIPE 로 죽는다. 이것이 정상 동작이다. 그리고 저자의 셸 글이 반복해서 강조하는 원칙 하나 — ls 출력을 파싱하지 말고 글롭으로 순회하라. 파일 이름에 공백이나 개행이 들어가는 순간 ls 파싱은 조용히 틀린다.

다음 실습에서 할 것

/root/lfcs-ess/ 에 디렉터리 트리를 세우고 하드 링크와 심볼릭 링크의 inode 차이를 직접 확인한다. find 를 크기·시각·권한 조건으로 조합해 목록을 뽑고, 같은 디렉터리를 gzip 과 xz 로 각각 압축해 크기를 비교한다. 마지막에는 diff -u 로 패치를 만들어 사본에 적용한다. 그다음 실습에서는 직접 만든 로그를 grep·sed·awk·jq·yq 로 가공한다.