리눅스 파일 시스템은 최상위 루트 /에서 시작하는 거대한 역트리(inverted tree) 구조다. 모든 파일·디렉터리는 이 트리 어딘가에 있고, 그 위치 표현이 경로다.
절대 경로(absolute path): 루트 /부터 전체를 명시. /home/user/docs/report.txt는 어디에서 입력하든 항상 같은 파일이다.
상대 경로(relative path): 현재 작업 디렉터리(current working directory)를 기준으로 한다.
기호
의미
.
현재 디렉터리
..
부모(상위) 디렉터리
~
현재 사용자의 홈 디렉터리
~user
특정 사용자(user)의 홈 디렉터리
-
직전(이전)에 있던 디렉터리 (cd -에서 사용)
예: 현재 위치가 /home/user/docs일 때 ../photos → /home/user/photos, ./report.txt → /home/user/docs/report.txt.
.과 ..의 정체..과 ..은 단순한 약속이 아니라 실제로 모든 디렉터리에 존재하는 하드 링크이다. ls -a 명령을 실행하면 이 두 항목이 실제 디렉터리 엔트리로 나타난다. 그래서 빈 디렉터리도 링크 수(link count)가 2이다. 자기 자신을 가리키는 . 하나와, 부모 디렉터리에서 자신을 가리키는 엔트리 하나가 합쳐진 결과이다.
pwd # 현재 작업 디렉터리의 절대 경로 출력cd /etc # /etc 디렉터리로 이동 (절대 경로)cd .. # 상위 디렉터리로 이동cd ~ # 홈 디렉터리로 이동cd # 인자 없이 cd만 입력해도 홈으로 이동cd - # 직전에 있던 디렉터리로 토글 이동
cd -를 반복하면 두 디렉터리를 토글처럼 왔다 갔다 할 수 있다 — 두 곳을 오가며 작업할 때 유용하다.
⚠️ 함정: pwd와 심볼릭 링크pwd에는 -P(physical)와 -L(logical) 옵션이 있다. 심볼릭 링크를 통해 디렉터리에 진입한 경우, 기본값인 -L은 링크 경로를 그대로 보여주지만 -P는 링크가 가리키는 실제 물리적 경로를 보여준다. 시험에서 이 둘의 차이를 묻는 문제가 종종 나온다.
ls(list)는 디렉터리 내용을 나열한다. 가장 많이 쓰는 명령이면서 옵션이 많아 헷갈리기 쉽다.
ls # 현재 디렉터리 내용 (숨김 파일 제외)ls -a # 숨김 파일(.으로 시작) 포함 모두 표시ls -l # 상세 정보(롱 포맷): 권한, 링크 수, 소유자, 크기, 시간ls -la # 상세 정보 + 숨김 파일ls -lh # 크기를 사람이 읽기 쉬운 단위(K, M, G)로 표시ls -lt # 수정 시간 순으로 정렬 (최신이 위)ls -ltr # 수정 시간 역순 (오래된 것이 위, 최신이 아래)ls -R # 하위 디렉터리까지 재귀적으로 표시ls -i # inode 번호 표시ls -d */ # 디렉터리 자체만 표시 (내용은 펼치지 않음)
ls -l 출력 첫 글자 = 파일 유형.
첫 글자
유형
-
일반 파일
d
디렉터리
l
심볼릭 링크
c
문자 장치
b
블록 장치
p
파이프
s
소켓
-h 옵션의 의미.-h는 human-readable의 약자이다. 1048576처럼 바이트 단위로 출력되던 크기를 1.0M처럼 변환한다. 단, -l과 함께 써야 의미가 있다. ls -h만 단독으로 쓰면 크기 정보 자체가 표시되지 않으므로 효과가 없다.
mkdir project # project 디렉터리 생성mkdir -p a/b/c # 중간 경로(a, b)가 없어도 한 번에 생성mkdir -m 755 secure # 권한을 755로 지정하며 생성rmdir empty_dir # 빈 디렉터리만 삭제 (내용 있으면 오류)rmdir -p a/b/c # c, b, a 순으로 빈 경로 전체 삭제
mkdir -p의 p는 parents. 깊은 경로를 한 번에 만들 때 필수다.
rmdir은 빈 디렉터리만 지운다. 내용이 있는 디렉터리를 통째로 지우려면 뒤에 나오는 rm -r를 써야 한다.
왜 rmdir은 빈 것만 지울까?rmdir은 시스템 호출 rmdir(2)을 그대로 반영한 명령이다. 이 시스템 호출은 안전성을 위해 의도적으로 빈 디렉터리만 제거하도록 설계되었다. 실수로 거대한 디렉터리 트리를 날려버리는 사고를 막기 위한 장치이다. 강력함과 위험을 분리한 유닉스 철학의 한 단면이다.
touch newfile.txt # 빈 파일 생성 (이미 있으면 시간만 갱신)touch a.txt b.txt c.txt # 여러 파일 동시 생성touch -t 202601011200 f.txt # 특정 시각(2026-01-01 12:00)으로 타임스탬프 설정touch -a f.txt # 접근 시간(atime)만 갱신touch -m f.txt # 수정 시간(mtime)만 갱신
본래 목적은 타임스탬프 갱신이지만, 없는 파일을 지정하면 빈 파일을 만들어주므로 빈 파일 생성 용도로 더 자주 쓰인다.
리눅스의 세 가지 시간. 모든 파일은 세 가지 타임스탬프를 가진다. atime(access time, 마지막 읽은 시각), mtime(modify time, 내용이 바뀐 시각), ctime(change time, inode 메타데이터가 바뀐 시각)이다. ctime은 권한이나 소유자가 바뀔 때도 갱신되며, 사용자가 임의로 과거 시각으로 설정할 수 없다는 점에서 atime/mtime과 다르다.
cp source.txt dest.txt # 파일 복사cp file.txt /backup/ # /backup 디렉터리로 복사cp -r dir1 dir2 # 디렉터리 재귀 복사 (하위 전체)cp -i a.txt b.txt # 덮어쓰기 전에 확인(interactive)cp -p file.txt copy.txt # 권한, 소유자, 타임스탬프 보존(preserve)cp -u src.txt dst.txt # 원본이 더 최신일 때만 복사(update)cp -a dir1 dir2 # 아카이브 모드: -dpR과 동등, 모든 속성 보존
헷갈리기 쉬운 것이 -r(recursive)다. 디렉터리를 복사하려면 반드시 -r(또는 -R)을 붙여야 하고, 없으면 omitting directory 오류가 난다.
⚠️ 함정: cp는 기본적으로 덮어쓴다
대부분의 리눅스 배포판에서 cp는 대상 파일이 이미 존재해도 경고 없이 덮어쓴다. 데이터를 잃지 않으려면 -i(interactive) 옵션으로 확인 프롬프트를 받거나, -n(no-clobber)으로 기존 파일을 보호해야 한다. 실무에서 한 줄의 명령이 중요한 파일을 날려버리는 일이 여기서 시작된다.
mv old.txt new.txt # 이름 변경 (같은 디렉터리 내)mv file.txt /tmp/ # 파일을 /tmp로 이동mv -i a.txt b.txt # 덮어쓰기 전 확인mv -f a.txt b.txt # 강제 덮어쓰기(force, 확인 없이)mv -u src dst # 원본이 더 최신일 때만 이동mv dir1 dir2 # 디렉터리 이동 (cp와 달리 -r 불필요)
같은 디렉터리 안에서 옮기면 이름 변경, 다른 디렉터리로 옮기면 이동이 된다.
디렉터리를 옮길 때 cp와 달리 -r 옵션이 필요 없다.
mv가 빠른 이유. 같은 파일 시스템 내에서 mv는 실제로 데이터를 복사하지 않는다. 단지 디렉터리 엔트리(파일 이름과 inode를 연결하는 정보)만 바꾼다. 그래서 수 기가바이트 파일도 순식간에 이동된다. 단, 다른 파일 시스템(다른 파티션, USB 등)으로 옮길 때는 복사 후 원본 삭제 방식으로 동작하므로 크기에 비례해 시간이 걸린다.
rm file.txt # 파일 삭제rm -i file.txt # 삭제 전 확인rm -r directory # 디렉터리와 그 내용 모두 재귀 삭제rm -f file.txt # 강제 삭제 (없는 파일이어도 오류 안 냄)rm -rf directory # 디렉터리 강제 재귀 삭제 (가장 위험)
rm -rf의 공포.rm -rf /는 시스템 전체를 파괴하는 명령으로 악명 높다. 특히 변수가 비어 있는 상태에서 rm -rf $DIR/처럼 쓰면 rm -rf /가 되어버릴 수 있다. 리눅스에는 휴지통 개념이 없어 한 번 지우면 복구가 매우 어렵다. rm을 쓸 때는 경로를 두 번 확인하고, 와일드카드(*)를 쓸 때는 특히 신중해야 한다.
파일 찾기는 크게 두 갈래 — 실시간으로 디스크를 탐색하는 find, 미리 만들어진 데이터베이스를 조회하는 locate.
find는 강력하지만 문법이 독특하다. 기본 형식은 find [경로] [조건] [동작].
find . -name "*.txt" # 현재 디렉터리 이하에서 .txt 파일 찾기find /home -name "report*" # 이름이 report로 시작하는 파일find . -iname "readme" # 대소문자 무시(insensitive)find . -type f # 일반 파일만 (f=file)find . -type d # 디렉터리만 (d=directory)find . -size +100M # 100MB보다 큰 파일find . -size -1k # 1KB보다 작은 파일find . -mtime -7 # 7일 이내 수정된 파일find . -mtime +30 # 30일보다 오래 전 수정된 파일find . -perm 644 # 권한이 정확히 644인 파일find . -user alice # 소유자가 alice인 파일find . -empty # 빈 파일/디렉터리
강력한 점은 -exec로 찾은 파일에 명령을 실행하는 데 있다.
find . -name "*.tmp" -exec rm {} \; # 찾은 .tmp 파일을 하나씩 삭제find . -name "*.log" -exec ls -l {} \; # 찾은 로그 파일 상세 정보 출력find . -name "*.txt" -exec grep "error" {} \; # 각 파일에서 error 검색find . -type f -exec chmod 644 {} + # 여러 파일을 한 번에 처리(+)
{} : 찾은 파일 이름으로 치환되는 자리표시자.
\; : 명령의 끝 표시. 대신 +를 쓰면 여러 파일을 한 번의 명령으로 묶어 처리해 효율적이다.
-exec의 \; vs +.\;는 찾은 파일마다 명령을 한 번씩 실행한다. 파일이 1000개면 명령이 1000번 실행된다. 반면 +는 가능한 많은 파일을 한 줄의 명령 인자로 묶어 실행한다. xargs와 비슷한 효율을 낸다. 대량 파일 처리 시 +가 훨씬 빠르지만, 명령이 파일을 개별적으로 처리해야 한다면 \;를 써야 한다.
locate는 접근 방식이 완전히 다르다. 시스템이 주기적으로(보통 하루 한 번 cron) 갱신하는 mlocate.db 데이터베이스를 조회하므로 매우 빠르다.
locate는 최신 파일을 못 찾는다.locate는 데이터베이스에 의존하므로, 방금 만든 파일은 updatedb로 DB를 갱신하기 전까지 찾을 수 없다. 반대로 이미 삭제된 파일이 결과에 남아 있을 수도 있다. "확실한 최신 결과"가 필요하면 느리더라도 실시간 탐색하는 find를 써야 한다.
which ls # ls 실행 파일의 경로 (PATH 내에서)which -a python # PATH 내 모든 일치 경로 표시whereis ls # 실행 파일 + 매뉴얼 + 소스 위치type cd # 명령의 종류(내장/외부/별칭) 표시
which : PATH에 등록된 디렉터리에서 실행 파일 위치를 찾는다.
whereis : 바이너리뿐 아니라 매뉴얼 페이지와 소스 코드 위치까지 알려준다.
which와 type의 차이.which는 외부 명령(디스크의 실행 파일)만 찾는다. 그래서 cd처럼 셸에 내장된 명령에는 which cd가 결과를 못 내놓는 경우가 있다. 반면 type cd는 "cd is a shell builtin"이라고 정확히 알려준다. 명령의 정체가 별칭인지, 내장인지, 외부 파일인지 궁금하면 type이 가장 정확하다.
# 1. 작업 공간 만들기mkdir -p ~/practice/week2/day1cd ~/practice/week2/day1pwd# 2. 파일 여러 개 만들고 목록 확인touch file1.txt file2.txt file3.logls -la# 3. 복사와 이동cp file1.txt backup.txtmkdir archivemv file3.log archive/ls -R# 4. 찾기 실습find . -name "*.txt"find . -type f -name "*.log"# 5. 명령어 위치 확인which lstype cd# 6. 정리cd ~rm -rf ~/practice
두세 번 반복하면 파일 시스템을 손끝으로 다루는 감각이 생긴다.
특히 find의 조건과 -exec 조합은 시험뿐 아니라 실무에서도 끝없이 쓰이니 충분히 연습해 둘 것.