본문 바로가기
TYLER SONGBlog
← Blog index
CS학습5 min read

cp: 파일과 디렉터리를 복사해 새로운 독립 사본을 만드는 명령

cp는 원본 파일이나 디렉터리의 내용을 읽어 새로운 파일로 써 넣는 리눅스 기본 명령이다. 복사 결과는 원본과 독립된 별도의 inode를 가지므로 이후 한쪽을 수정해도 다른 쪽에 영향이 없다. -r, -p, -i, -n, -a 같은 옵션으로 디렉터리 복사, 속성 보존, 덮어쓰기 방지를 제어할 수 있다.

By 송민성

1. 개념

cp(copy)는 파일이나 디렉터리를 다른 위치로 복사하는 명령이다. 기본 형식은 다음과 같다.

text
cp [옵션] 원본 대상
cp [옵션] 원본1 원본2 ... 대상디렉터리/

복사가 끝나면 원본과 사본은 내용만 같을 뿐 서로 다른 파일이다. 파일 이름을 바꾸거나 위치를 옮기는 mv, 같은 데이터를 가리키는 이름을 추가하는 하드 링크(ln)와는 구별된다.

2. 왜 사용하는가

  • 설정 파일을 수정하기 전에 백업을 만든다.
  • 배포용 산출물이나 템플릿을 다른 디렉터리에 배치한다.
  • 원본을 보존한 채 실험하거나 테스트 데이터를 복제한다.
  • 스크립트에서 파일을 정해진 위치로 옮겨 놓는 작업을 자동화한다.

3. 동작 원리

일반 파일을 복사할 때 cp는 대략 다음 순서로 동작한다.

  1. 원본을 읽기 모드로 연다(open).
  2. 대상 파일을 쓰기 모드로 연다. 없으면 새로 만들고, 있으면 기존 내용을 잘라내고(truncate) 덮어쓴다.
  3. 원본에서 데이터를 읽어 대상에 쓴다(read/write). 구현에 따라 copy_file_range 같은 시스템 호출이나 파일시스템의 reflink 기능을 사용하기도 한다.
  4. 파일 권한 등 메타데이터를 설정하고 파일을 닫는다.

핵심은 새 inode가 만들어진다는 점이다. inode는 파일의 메타데이터와 데이터 블록 위치를 가리키는 구조체로, 사본은 원본과 다른 inode를 갖는다.

기본 동작에서 소유자는 cp를 실행한 사용자가 되고, 권한은 원본 권한에 현재 사용자의 umask가 적용된 값이 된다. 수정 시각도 복사한 시점으로 새로 기록된다. 이를 원본과 같게 유지하려면 -p나 -a 옵션을 쓴다.

디렉터리는 -r(또는 -R) 옵션을 주면 내부를 재귀적으로(recursive) 순회하며 하위 항목을 모두 복사한다.

4. 코드 예제

실습용 디렉터리에서 그대로 실행할 수 있는 예제다.

shell
# 실습 환경 준비 mkdir -p /tmp/cp-demo/src/sub && cd /tmp/cp-demo echo "hello" > src/a.txt echo "world" > src/sub/b.txt # 1) 파일 하나 복사 cp src/a.txt copy_a.txt # 2) 여러 파일을 디렉터리로 복사 mkdir dest cp src/a.txt copy_a.txt dest/ # 3) 디렉터리 복사 (-r 필수) cp -r src dest_src # 4) 덮어쓰기 전에 확인 (-i), 덮어쓰지 않기 (-n) cp -i src/a.txt copy_a.txt cp -n src/a.txt copy_a.txt # 5) 복사 과정 출력 (-v) cp -v src/a.txt verbose_a.txt # 6) 권한·소유자·시각 보존 (-p), 아카이브 모드 (-a) cp -p src/a.txt preserved_a.txt cp -a src archive_src # 7) 사본이 독립된 파일인지 확인 (inode 번호가 다름) ls -i src/a.txt copy_a.txt # 8) 백업 파일 만들기 cp --backup=numbered src/a.txt copy_a.txt # GNU cp 전용

--backup은 GNU coreutils 옵션이라 macOS 기본 cp에서는 지원되지 않는다. 자주 쓰는 옵션은 다음과 같다.

옵션의미
-r, -R디렉터리를 재귀적으로 복사
-i덮어쓰기 전에 확인 질문
-n기존 파일을 덮어쓰지 않음
-v복사 내역 출력
-p권한, 소유자, 시각 보존 시도
-a아카이브 모드 (GNU에서는 -dR --preserve=all 에 해당)

5. 시간 복잡도 또는 성능 특성

복사 비용은 대체로 복사할 데이터의 총 크기에 비례한다. 파일 크기를 n바이트라 하면 데이터 이동은 O(n)이다. 디렉터리를 복사할 때는 여기에 파일 개수만큼의 open, stat, 디렉터리 항목 생성 비용이 더해지므로, 작은 파일이 매우 많으면 총 용량이 같아도 큰 파일 하나보다 느리다.

실제 속도는 디스크 종류, 페이지 캐시(page cache), 파일시스템에 좌우된다. Btrfs, XFS 등 reflink를 지원하는 파일시스템에서 GNU cp의 --reflink=auto를 쓰면 데이터 블록을 실제로 복사하지 않고 공유하여 거의 즉시 끝날 수 있다. 이후 한쪽이 수정되면 그때 해당 블록만 분리된다(copy-on-write).

6. 실무 사용 사례

  • 설정 변경 전 백업: cp nginx.conf nginx.conf.bak
  • 환경 변수 템플릿 복제: cp .env.example .env
  • 빌드 산출물 배포: cp -r dist/. /var/www/html/
  • Dockerfile이나 CI 스크립트에서 파일 배치
  • 원격 서버로의 복사는 cp가 아니라 scp나 rsync를 사용한다.

7. 주의할 점

  • 기본 동작은 덮어쓰기다. 대상 파일이 있으면 경고 없이 내용이 사라진다. 중요한 곳에는 -i나 -n을 쓴다.
  • 디렉터리는 `-r` 없이 복사되지 않는다. cp: -r not specified; omitting directory 같은 메시지가 나온다.
  • 끝의 슬래시와 대상 존재 여부에 따라 결과가 달라진다. cp -r src dest에서 dest가 이미 있으면 dest/src가 만들어지고, 없으면 dest가 src의 사본이 된다.
  • 심볼릭 링크 처리가 다르다. -r만 쓸 때의 링크 처리 방식은 구현마다 다를 수 있으므로, 링크 자체를 유지하려면 -a나 -P를, 가리키는 대상을 복사하려면 -L을 명시한다.
  • 기본 복사는 메타데이터를 완전히 보존하지 않는다. 소유자, 시각, 확장 속성까지 유지해야 하면 -a를 쓴다.
  • 대용량 복사나 중단 후 재개가 필요하면 rsync가 더 적합하다.
  • **와일드카드(*)는 셸이 확장한다.** 숨김 파일(.으로 시작)은 *에 포함되지 않는다.

8. 핵심 정리

  • cp 원본 대상은 내용을 읽어 새 inode를 가진 독립된 파일을 만든다.
  • 디렉터리는 -r, 속성 보존은 -p, 전체 보존은 -a를 쓴다.
  • 기본값은 조용한 덮어쓰기이므로 -i, -n으로 방어한다.
  • 복사 비용은 데이터 크기에 비례(O(n))하며, 작은 파일이 많으면 파일당 오버헤드가 커진다.
  • 옵션 일부는 GNU와 BSD(macOS) 구현이 달라, 이식성이 필요하면 공통 옵션만 사용한다.