터미널 기초: 경로, 파일 다루기, 공백 들어간 파일명 함정
macOS 터미널에서 pwd·ls·cd·mkdir·cp·mv·rm 을 실제로 실행. 진짜 에러 메시지, rm -r 새 폴더 가 엉뚱한 폴더를 지운 사례, cp 와 mv 속도 차이 실측, 연습 문제까지.
시리즈: 명령줄 강좌 1/6 (초급). 터미널을 처음 열어 보는 사람, 블로그에서 명령을 복사해 붙여 넣기만 해 본 사람을 위한 글입니다. 다 읽으면 폴더 사이를 이동하고, 파일을 만들고 복사·이동·삭제할 수 있으며, 엉뚱한 폴더를 지우는 사고를 막는 따옴표 규칙 하나를 확실히 알게 됩니다.
입사 첫날 받은 맥북에서 README 의 "터미널을 열고 다음을 실행하세요" 앞에서 멈춘 신입 개발자를 위한 글입니다. 까만 창의 정체, 처음 5분 동안 칠 명령, 그리고 뭔가 잘못됐을 때 실제로 나오는 에러 메시지를 그대로 보여 줍니다. 특히 윈도우 쓰던 동료에게서 받은 압축 파일에 흔히 들어 있는 "새 폴더" 처럼 이름에 공백이 있는 폴더를 다룰 때, 우리 실험에서 rm -r 새 폴더 는 지우려던 폴더가 아니라 전혀 다른 "폴더" 라는 폴더를 안의 파일째 지웠습니다.
무엇을 어떤 순서로 배우면 좋은지 큰 그림은 신입 개발자를 위한 학습 로드맵에 정리해 두었습니다.
실행 환경: macOS 26.6, Apple Silicon Mac(M1 Pro), zsh 5.9(macOS 기본 셸)와 bash 3.2.57, macOS 에 들어 있는 BSD 판 ls·cp·mv·rm. 아래 블록은 모두 실제로 실행한 결과입니다. 출력에서 홈 폴더 경로는 ~ 로, 로그인 이름은 demo 로 바꿨고 그 밖에는 손대지 않았습니다.
터미널, 셸, 명령: 첫날 듣게 될 세 단어
- 터미널은 창입니다(맥의 '터미널' 앱, iTerm2, VS Code 아래쪽 패널). 글자를 보여 주고 키 입력을 어딘가로 보낼 뿐입니다.
- 셸은 그 창 안에서 돌아가는 프로그램으로, 입력을 읽어 뜻을 해석하고 다른 프로그램을 실행합니다. macOS 는 Catalina(10.15)부터 zsh 가 기본입니다. 예전 bash 도 들어 있지만 2007년의 3.2 버전에 머물러 있습니다.
- 명령은 디스크에 있는 프로그램(
/bin/ls)이거나 셸에 내장된 기능(cd)입니다.type으로 구분합니다.
~/demo % type ls cd
ls is /bin/ls
cd is a shell builtin
~/demo % 는 프롬프트로, "입력하세요" 라는 신호입니다. 현재 위치(~/demo)를 보여 주고 zsh 는 %, bash 는 $ 로 끝납니다. 프롬프트는 직접 입력하지 않습니다. 아래에서 ~/… % 로 시작하는 줄이 입력이고, 그 밑이 돌아온 출력입니다.
처음 5분: pwd, ls, cd, mkdir
pwd(print working directory)는 "지금 있는 폴더"를 알려 줍니다. 따로 지정하지 않으면 모든 명령은 이 폴더를 기준으로 움직입니다.
~/demo % pwd
~/demo
~/demo % mkdir notes
~/demo % mkdir projects/app
mkdir: projects: No such file or directory
~/demo % mkdir -p projects/app/src
~/demo % ls -F
notes/
projects/
mkdir 은 폴더를 만들지만 한 단계씩만 만듭니다. projects 가 아직 없어서 projects/app 이 실패했습니다. 옵션(명령 뒤에 붙는 하이픈 글자) -p 를 주면 중간 폴더까지 한꺼번에 만듭니다. ls -F 는 폴더 이름 뒤에 / 를 붙여 파일과 구분하기 쉽게 합니다.
이동은 cd(change directory)입니다.
~/demo % cd projects/app
~/demo/projects/app % cd ..
~/demo/projects % cd -
~/demo/projects/app
~/demo/projects/app % cd ~
~ % cd ~/demo
.. 은 한 단계 위, cd - 는 바로 전에 있던 곳, ~ 는 홈 폴더입니다. cd 할 때마다 프롬프트가 바뀌니 그것만 봐도 현재 위치를 알 수 있습니다. 타이핑을 줄이려면 Tab 키(이름 자동 완성)와 위쪽 화살표(이전 명령 불러오기)를 쓰세요.
경로: 절대 경로, 상대 경로, ~, ., ..
경로는 파일의 주소입니다. 같은 파일을 세 가지로 쓸 수 있습니다.
| 형식 | 시작점 | 예 | 깨지는 경우 |
|---|---|---|---|
| 절대 경로 | 디스크 맨 위 / |
/<홈>/demo/notes/todo.txt (pwd 가 보여 주는 전체 경로) |
파일을 옮기거나 다른 컴퓨터에서 돌릴 때 |
| 홈 기준 | 홈 폴더 ~ |
~/demo/notes/todo.txt |
~ 를 따옴표 안에 넣을 때 |
| 상대 경로 | 지금 폴더 | ../notes/todo.txt |
먼저 다른 곳으로 cd 했을 때 |
주의할 점이 하나 있습니다. ~ 를 펼치는 건 셸이고, 따옴표 밖에 있을 때만 펼칩니다.
~/demo % ls -ld ~/demo
drwxr-xr-x@ 3 demo wheel 96 Nov 12 17:09 ~/demo
~/demo % ls -ld "~/demo"
ls: ~/demo: No such file or directory
~/demo % ls -ld "$HOME/demo"
drwxr-xr-x@ 3 demo wheel 96 Nov 12 17:09 ~/demo
따옴표 안에서는 ls 가 이름이 "~" 인 폴더를 찾습니다. 공백 때문에 따옴표가 필요하면 "~/…" 대신 "$HOME/…" 를 쓰세요.
복사, 이동, 삭제: cp, mv, rm 과 에러 메시지
~/demo % echo "first line" > notes/todo.txt
~/demo % cat notes/todo.md
cat: notes/todo.md: No such file or directory
~/demo % cp notes/todo.txt notes/todo-backup.txt
~/demo % cp notes backup
cp: notes is a directory (not copied).
~/demo % cp -R notes backup
~/demo % mv notes/todo-backup.txt notes/old.txt
~/demo % rm backup
rm: backup: is a directory
~/demo % rmdir backup
rmdir: backup: Directory not empty
~/demo % rm -r backup
에러 메시지는 정확하니 영어라도 한 단어씩 읽으세요. cp 는 폴더에 -R(재귀)이 필요하고, mv 는 이름 바꾸기와 옮기기를 겸하며, rm 은 파일만 지우고 폴더는 거부하고, rm -r 은 폴더를 내용째 지우고, rmdir 은 빈 폴더만 지웁니다(그래서 "비어 있어야 할" 폴더에는 안전합니다). ls -l 권한 칸 뒤의 @ 는 확장 속성이 있다는 표시인데, xattr 로 보면 여기서는 macOS 가 스스로 붙인 com.apple.provenance 였고 해는 없습니다.
중요: rm 은 휴지통을 거치지 않습니다. 되돌릴 수 없습니다. 익숙해질 때까지는 rm -i(remove a.txt? 하고 물어봄)를 쓰세요.
자주 하는 실수: 공백 있는 이름 때문에 엉뚱한 폴더가 지워진다
셸은 입력을 공백마다 단어로 나눈 다음 명령에 넘깁니다. 이름 안의 공백은 구분용 공백과 구별되지 않습니다.
~/demo/ko % mkdir "새 폴더"
~/demo/ko % mkdir 폴더
~/demo/ko % echo keep > 폴더/중요.txt
~/demo/ko % rm -r 새 폴더
rm: 새: No such file or directory
~/demo/ko % ls
새 폴더
~/demo/ko % ls 폴더
ls: 폴더: No such file or directory
실제로 일어난 일: rm 은 '새'와 '폴더' 두 단어를 받았습니다. '새'는 없으니 에러가 났고, 그래서 얼핏 "아무 일도 없었다"처럼 보입니다. 그런데 '폴더'는 있었기 때문에 rm -r 이 중요.txt 까지 통째로 지웠습니다. 지우려던 "새 폴더"는 그대로 남아 있습니다. 따옴표 없이 cd 새 폴더 를 쳐도 cd: string not in pwd: 새 가 나오고, bash 3.2 에서는 bash: cd: …: No such file or directory 형태로 나옵니다.
해결: 공백이 들어갈 수 있는 이름은 반드시 따옴표로 감싸거나, 공백 앞에 백슬래시를 붙입니다. 세 가지 모두 됩니다.
~/demo/ko % cd "새 폴더"
~/demo/ko/새 폴더 % cd ..
~/demo/ko % cd 새\ 폴더
~/demo/ko/새 폴더 % cd ..
~/demo/ko % cat '새 폴더'/메모.txt
draft
사고를 막는 습관: rm -r 전에 같은 따옴표를 쓴 이름으로 ls -d "이름" 을 먼저 실행합니다. 딱 하나만 보이면 위쪽 화살표로 불러와 ls -d 를 rm -r 로 바꿔 실행합니다. Tab 자동 완성을 쓰면 백슬래시도 알아서 들어갑니다.
작은 함정 두 가지: 하이픈으로 시작하는 이름, zsh 의
- 로 시작하는 파일 이름은 옵션으로 오해받습니다.
~/demo % rm -n.txt
rm: illegal option -- n
usage: rm [-f | -i] [-dIPRrvWx] file ...
unlink [--] file
~/demo % rm -- -n.txt
-- 는 "여기부터는 옵션이 아니다"라는 뜻입니다. ./-n.txt 라고 써도 지워집니다.
주석 달린 명령을 zsh 에 붙여 넣으면 블로그에 흔한 ls # 목록 의 # 이 주석으로 처리되지 않습니다.
~/demo % ls # list files
ls: #: No such file or directory
ls: files: No such file or directory
ls: list: No such file or directory
개인 설정 없이 macOS 기본 /etc/zshrc 만 읽은 상태에서 확인했습니다. ~/.zshrc 에 setopt interactive_comments 를 넣으면 bash 처럼 # 뒤가 무시됩니다. 스크립트 파일 안에서는 언제나 주석입니다.
도움말 보기: --help 가 아니라 man
리눅스라면 ls --help 가 되지만, 맥의 BSD 판은 이렇게 반응합니다.
~/demo % ls --help
ls: unrecognized option `--help'
usage: ls [-@ABCFGHILOPRSTUWXabcdefghiklmnopqrstuvwxy1%,] [--color=when] [-D format] [file ...]
~/demo % mkdir --help
mkdir: illegal option -- -
usage: mkdir [-pv] [-m mode] directory_name ...
~/demo % cd --help
cd: no such file or directory: --help
사용법 한 줄도 쓸모는 있습니다. 전체 설명은 man ls 로 봅니다(스페이스는 다음 쪽, / 는 검색, q 는 종료). 옵션 하나만 바로 찾고 싶으면 man ls | col -b | grep -n -A2 '^ -a' 처럼 grep 과 엮습니다(-a 는 ". 으로 시작하는 항목도 포함"이라고 나옵니다). cd 같은 내장 명령은 zsh 에서는 man zshbuiltins, bash 에서는 help cd 입니다.
| 하고 싶은 일 | 명령 | 안전 습관 |
|---|---|---|
| 지금 위치 확인 | pwd |
cd 뒤에는 프롬프트를 본다 |
| 숨김 파일까지 보기 | ls -la |
. 으로 시작하면 숨김 파일 |
| 중첩 폴더 만들기 | mkdir -p a/b/c |
공백 있는 이름은 따옴표 |
| 폴더 복사 | cp -R 원본 대상 |
대상이 이미 있는지 확인 |
| 이름 변경·이동 | mv 옛이름 새이름 |
mv -i 로 덮어쓰기 전 확인 |
| 파일 삭제 | rm 파일 |
익숙해질 때까지 rm -i |
| 폴더 삭제 | rm -r "이름" |
같은 따옴표로 ls -d 먼저 |
| 빈 폴더 삭제 | rmdir 이름 |
비어 있지 않으면 안전하게 실패 |
실측: 파일 1만 개 복사 vs 이동
커다란 폴더도 mv 는 순식간에 끝나는데 cp 는 몇 초씩 걸리는 이유가 뭘까요. 200~8,000 바이트짜리 파일 1만 개(디스크상 59 MB)를 고정 시드로 만들고, 같은 APFS 볼륨에서 cp -R, mv, rm -r 을 각각 5번 쟀습니다.
#!/bin/zsh -f
# bench.zsh 작업폴더 - 작은 파일 1만 개로 cp -R / mv / rm -r 비교
zmodload zsh/datetime
cd "${1:?work dir}" || exit 1
rm -rf src copy moved
python3 - <<'PY'
import os, random
rng = random.Random(42)
for i in range(10000):
d = f"src/d{i % 100:02d}"
os.makedirs(d, exist_ok=True)
with open(f"{d}/f{i:05d}.txt", "w") as f:
f.write("x" * rng.randint(200, 8000))
PY
t() { local s=$EPOCHREALTIME; "$@"; printf '%.1f\n' $(( (EPOCHREALTIME - s) * 1000 )); }
for r in 1 2 3 4 5; do
sync
a=$(t cp -R src copy)
b=$(t mv copy moved)
c=$(t rm -r moved)
print "run $r: cp -R ${a} ms | mv ${b} ms | rm -r ${c} ms"
done
| 작업 (파일 1만 개, 59 MB) | 5회 중앙값 | 범위 |
|---|---|---|
cp -R src copy |
5,692 ms | 3,749~6,708 ms |
cp -cR src clone (APFS 클론) |
1,885 ms | 1,746~2,003 ms |
rm -r |
953 ms | 776~1,149 ms |
mv copy moved |
2.8 ms | 2.5~3.2 ms |
같은 볼륨 안의 mv 는 데이터를 건드리지 않고 디렉터리 항목 하나만 고치기 때문에, 파일 1만 개도 3 ms 가 안 걸렸습니다. cp -R 은 파일 1만 개를 새로 만들고 59 MB 를 씁니다. macOS 의 cp -c 는 clonefile(2) 를 써서 내용이 바뀔 때까지 데이터 블록을 공유하므로 약 3배 빠르지만, 파일 항목 1만 개는 여전히 만들어야 합니다. USB 메모리 같은 다른 디스크로의 mv 는 실제로는 복사 후 삭제라서 cp 이상 걸립니다. cp -R 값이 들쭉날쭉한 건 같은 컴퓨터에서 다른 작업이 돌고 있었기 때문이고, 네 작업의 순위는 다섯 번 모두 같았습니다.
솔직한 한계와 버전 메모
- macOS 26.6, zsh 5.9, bash 3.2.57 에서만 확인했습니다. 에러 문구는 이 버전 BSD 도구의 것이고, 리눅스(GNU coreutils)는 문구가 다릅니다. 예:
cannot remove 'backup': Is a directory(여기서는 실행 안 함). --help는 리눅스 GNU coreutils 에서는 됩니다(여기서는 실행 안 함). macOS 에서는man을 쓰세요.interactive_comments동작은 macOS 26.6 기본/etc/zshrc로 확인했습니다. 회사에서 받은 맥에는 이 옵션을 켜 둔.zshrc가 이미 있을 수도 있습니다.cp -c는 macOS 전용 옵션이고 이 버전에서만 확인했습니다.- 측정은 컴퓨터 1대, 볼륨 1개, 5회, 다른 작업이 함께 돌던 상태입니다. "여기서는 mv 가 약 2,000배 빠르다"는 비율을 교훈으로 삼으세요.
연습 문제
~/demo안에trip/2026 photos폴더를 만들고, 그 안에 빈 파일day 1.txt를 만든 뒤trip아래를 전부 출력하세요.~/demo/projects/app에 있을 때cd ../../notes는 어디로 가나요? 그 폴더가 없으면 어떤 에러가 나오나요?Old Files라는 폴더를 지우려고 합니다. 실행할 명령 두 개를 순서대로 쓰세요.
정답
- 이름을 따옴표로 감쌉니다(
touch는 빈 파일을 만들고,ls -R은 하위 폴더까지 나열합니다).
~/demo % mkdir -p "trip/2026 photos"
~/demo % touch "trip/2026 photos/day 1.txt"
~/demo % ls -R trip
2026 photos
trip/2026 photos:
day 1.txt
- 첫
..으로projects, 두 번째로~/demo로 올라간 뒤notes로 들어가므로~/demo/notes입니다. 없으면 zsh 는cd: no such file or directory: ../../notes를 출력하고 원래 자리에 머뭅니다. - 먼저 확인하고, 똑같이 따옴표를 쓴 이름으로 지웁니다.
~/demo % ls -d "Old Files"
Old Files
~/demo % rm -r "Old Files"
다음 강좌: 강좌 2: 파이프, 리다이렉션, 텍스트 도구. 한 명령의 출력을 다음 명령의 입력으로 잇는 방법입니다. 이어지는 강좌: find·xargs·sed·awk, 변수·스크립트·종료 코드. 학습 로드맵으로 돌아가기.
자주 묻는 질문
폴더가 있는데 cd 새 폴더 가 실패하는 이유는?
셸은 cd 를 실행하기 전에 공백에서 단어를 나누기 때문에 cd 는 '새'와 '폴더' 두 단어를 받습니다. zsh 는 'cd: string not in pwd: 새' 라고 답합니다. 이름을 따옴표로 감싸거나(cd "새 폴더") 공백 앞에 백슬래시를 붙이세요(cd 새\ 폴더).
rm 으로 지운 파일은 휴지통으로 가나요?
아니요. rm 은 이름을 즉시 지우고 셸에서 되돌릴 방법이 없습니다. 익숙해질 때까지는 rm -i 로 확인을 받고, 폴더를 지울 때는 같은 따옴표를 쓴 이름으로 ls -d 를 먼저 실행한 뒤 rm -r 합니다.
맥에서 ls --help 가 안 되는 이유는?
macOS 의 ls·cp·mkdir 은 BSD 판이라 --help 옵션이 없습니다. 'unrecognized option' 이나 'illegal option -- -' 과 한 줄짜리 사용법을 출력합니다. 자세한 설명은 man ls 로 보고 q 로 빠져나옵니다.