【Linux】cpコマンドでディレクトリごとコピーする方法|-rと-aの違い・中身だけコピー・強制上書きまで

実務レシピ

こんにちは、Bash玄(ばっしゅげん)です。

Linuxでシステムの構築やデプロイ、バックアップを行う際、最も頻繁に使うのが cp コマンド です。しかし、「ディレクトリを丸ごとコピーしたいだけなのに、思わぬ挙動で作業が止まった」という経験はありませんか?

実務では、 omitting directory エラーでコピーできない」「コピー先が存在していたためディレクトリが二重に入れ子になった」「所有者や更新日時が変わりプログラムが動かなくなった」「上書き確認プロンプトが何十回も出てエンターを連打した」 といったトラブルが日常茶飯事です。

本記事では、Google強調スニペット対応の 最速コマンド早見表 をはじめ、エラーの解消法、入れ子を防いで中身だけコピーする指定法(src/.)、本番作業で必須の属性保持(-a)、上書き確認をスキップする強制上書き(\cp)、階層保持の --parents まで、実務で即役立つ手順を徹底解説します。

  1. 【結論】ディレクトリごとコピーする最速コマンド早見表
    1. 単純コピー(cp -r)
    2. 属性保持バックアップ(cp -a)※推奨
    3. ディレクトリの「中身だけ」コピー(cp -a src/. dst/)
  2. cpコマンドでディレクトリをコピーする基本構文とエラー原因
    1. 基本構文:cp [オプション] コピー元 コピー先
    2. なぜ「cp: omitting directory」エラーが出るのか?
    3. 再帰的コピーを指定する -r と -R の違い(同等動作)
  3. 【超重要】コピー先の「有無」による挙動の違いと「中身だけコピー」
    1. コピー先が存在しない場合:指定したディレクトリ名で新規作成
    2. コピー先が既に存在する場合:内部に入れ子(dst/src)でコピーされる罠
    3. ディレクトリ自体ではなく「中身だけ」を展開コピーする正解記法(src/. の利用)
    4. src/* と src/. の決定的な違い(隠しファイル .env や .gitignore のコピー漏れ防止)
  4. -r と -a の違い(なぜバックアップには -a を使うべきなのか)
    1. -r オプションの弱点(所有者・タイムスタンプが実行ユーザー・現在日時に変更される)
    2. -a(アーカイブモード)が保持する属性一覧(-p, -d, -r の完全統合)
    3. Webサーバー移行や設定ファイル退避で -a が必須となる理由
  5. 実務で必須の主要オプション完全ガイド
    1. -p: パーミッション・所有者・タイムスタンプの個別保持
    2. -i: 上書き時の確認プロンプト表示
    3. -f および \cp: 強制上書き(alias cp=’cp -i’ の一時無効化テクニック)
    4. -v: コピー進捗・対象ファイルの詳細ログ表示
    5. -u: 更新日付が新しいファイルのみコピー(簡易増分同期)
    6. –parents: 親ディレクトリの階層構造を維持してコピー
  6. よくあるトラブルと解決策
    1. シンボリックリンクが実体コピーされて容量が肥大化する問題(-d / -a の使い分け)
    2. パーミッションエラー(Permission denied)とsudo実行時の注意点
  7. 大量データや定期同期なら「rsync」を使うべき理由
    1. 差分転送による圧倒的な速度差と中断再開
  8. まとめ
    1. 関連おすすめ記事
  9. 関連記事

【結論】ディレクトリごとコピーする最速コマンド早見表

まずは結論です。実務でよく使われるディレクトリコピーの代表パターンと実行コマンドをまとめました。迷ったらこの早見表から最適なコマンドを選んでください。

やりたいこと(ユースケース)実行コマンド特徴・実務での用途
単純コピー(テスト環境等)cp -r src/ dst/基本の再帰コピー。所有者や更新日時は実行時の情報に変更される
属性保持バックアップ(本番推奨)cp -a src/ dst/権限・所有者・更新日時・リンクを完全保持。バックアップの鉄則
ディレクトリの「中身だけ」コピーcp -a src/. dst/二重ディレクトリ(入れ子)を防ぎ、.env 等の隠しファイルも完全複製
上書き確認なしで強制上書き\cp -rf src/ dst/alias cp='cp -i' を無効化し、確認プロンプトなしで一括上書き
更新日時が新しい差分のみコピーcp -au src/ dst/コピー先より新しいファイルだけを更新する簡易増分同期
親ディレクトリの階層構造を保持cp -a --parents path/dir dst/深い階層構造をコピー先にもそのまま再現して複製

単純コピー(cp -r)

ファイル内容の複製だけで十分であり、作成日時や所有者を気にする必要がない一時的な検証作業であれば、基本の -r(recursive:再帰的) を使用します。

# ディレクトリ src を dst へ丸ごと再帰コピーする
cp -r src/ dst/

ただし、-r は所有者が実行ユーザーに変わり、タイムスタンプも「実行した現在時刻」に書き換わります。本番環境のデータ移行やバックアップには適していません。

属性保持バックアップ(cp -a)※推奨

実務でシステムのバックアップや設定ファイルの退避を行う場合は、 必ず -a(archive:アーカイブモード)を使うのが鉄則 です。

# パーミッション、所有者、タイムスタンプ、リンクを完全に保持してコピー(最推奨)
cp -a src/ dst/

-a は、「属性保持(-p)」「シンボリックリンク維持(-d)」「再帰コピー(-r)」がすべて統合された強力なオプションです。元の状態を寸分違わず複製できます。

ディレクトリの「中身だけ」コピー(cp -a src/. dst/)

ディレクトリの名前そのものではなく、その 「中身(配下の全ファイル・サブディレクトリ)」だけを既存ディレクトリの直下に展開したい 場合は、コピー元の末尾に /. を付けます。

# src 自体ではなく、src の中身すべて(隠しファイル含む)を dst 直下に展開コピーする
cp -a src/. dst/

この src/. 指定により、二重ディレクトリ化(入れ子)を確実に防ぎつつ、後述する隠しファイル(.env 等)も漏れなく展開できます。

cpコマンドでディレクトリをコピーする基本構文とエラー原因

cp コマンドでディレクトリを扱う際の基本ルールと、初心者が必ず直面するエラーのメカニズムを解説します。

基本構文:cp [オプション] コピー元 コピー先

Linuxにおける cp(copy)コマンドの基本構文は以下の通りです。

cp [オプション] コピー元(Source) コピー先(Destination)

第1引数にコピーしたい対象、第2引数にコピー先を指定します。単一ファイルのコピーであればオプションなしで実行できますが、ディレクトリを対象とする場合は再帰オプションの指定が必須です。

なぜ「cp: omitting directory」エラーが出るのか?

ディレクトリに対してオプションなしで cp を実行すると、以下のエラーが発生して処理が中断されます。

$ cp my_folder backup/
cp: -r not specified; omitting directory 'my_folder'
# または環境によって
cp: omitting directory 'my_folder'

「omitting directory」とは、 「ディレクトリを省略(除外)しました」 という意味です。

Linuxのファイルシステムにおいて、ディレクトリは「ファイル名とi-node番号の対応表」を格納する特殊なファイルです。ディレクトリを複製するには、内部の全ファイルを階層的にたどって処理する「再帰処理」が必要となります。

大容量ディレクトリを誤って複製する事故を防ぐ安全装置として、cp コマンドは再帰オプション(-r-a)が明示されない限りディレクトリのコピーを拒否する仕様になっています。

再帰的コピーを指定する -r と -R の違い(同等動作)

再帰コピーのオプションには小文字の -r と大文字の -R が存在します。結論から言うと、 現代の一般的なLinux(GNU Coreutils)では両者は完全に同等 です。

# どちらを実行しても全く同じ動作をする
cp -r src/ dst/
cp -R src/ dst/

POSIX標準規格で正式に定められているのは大文字の -R です。古いUNIX系OSでは特殊ファイルの扱いにわずかな差異がありましたが、現在のLinux環境(Ubuntu, RHEL, CentOS, Debian, Alpine等)ではどちらを使っても同じ処理が実行されます。普段の入力では打ちやすい小文字の -r で問題ありません。

【超重要】コピー先の「有無」による挙動の違いと「中身だけコピー」

実務でエンジニアが最も混乱し、シェルスクリプトの不具合原因になりやすいのが、 「コピー先ディレクトリが存在するかどうかで展開先が変わる」 という仕様です。

コピー先が存在しない場合:指定したディレクトリ名で新規作成

指定したコピー先ディレクトリがまだ存在しない場合、コピー元の内容がその名前で新規作成されます。

# コピー先の dst が存在しない場合
cp -r src dst

この場合、新しく dst/ が作成され、その配下に src/ の中身(ファイル群)が直接配置されます。

コピー先が既に存在する場合:内部に入れ子(dst/src)でコピーされる罠

一方、 コピー先の dst/ が既に存在している場合 、同じコマンドを実行すると dst/ の中に src/ ディレクトリごと格納されてしまいます。

# コピー先の dst が既に存在する場合
cp -r src dst

以下のASCIIアートで、ディレクトリ構造の違いを直感的に比較してみましょう。

【ケース1:コピー先 dst が存在しない場合】
実行前:
├── src/
│   ├── file1.txt
│   └── file2.txt
└── (dst は存在しない)

実行後(cp -r src dst):
├── src/
└── dst/              <-- src の中身が dst 直下にきれいに展開される
    ├── file1.txt
    └── file2.txt

--------------------------------------------------

【ケース2:コピー先 dst が既に存在する場合(入れ子の罠!)】
実行前:
├── src/
│   ├── file1.txt
│   └── file2.txt
└── dst/              <-- 既にディレクトリが存在している

実行後(cp -r src dst):
├── src/
└── dst/
    └── src/          <-- ★ dst の中に src ディレクトリが丸ごと入って二重化!
        ├── file1.txt
        └── file2.txt

この仕様を理解していないと、定期デプロイやバックアップスクリプトを2回目に実行した際、dst/src/src/... と無限に入れ子が増殖するトラブルを引き起こします。

ディレクトリ自体ではなく「中身だけ」を展開コピーする正解記法(src/. の利用)

コピー先が存在するかどうかに左右されず、 「常にディレクトリの中身だけをコピー先の直下に展開したい」 ときの正解記法が、末尾に /. を付与するテクニックです。

# コピー元の中身だけを既存の dst 直下へ確実に展開する
cp -a src/. dst/

Linuxにおいて .(ドット1つ)は「そのディレクトリ自身の中身」を表します。

src/. と指定することで、cp コマンドに対して「src という入れ物ではなく、中身の全要素をコピーせよ」と明示できます。これにより、dst/ が既に存在していても余計な階層を作らず、中身だけをマージ展開できます。

src/* と src/. の決定的な違い(隠しファイル .env や .gitignore のコピー漏れ防止)

「中身だけコピーするなら cp -a src/* dst/ でも良いのでは?」と思うかもしれません。しかし、これには 重大なリスク があります。

シェルの仕様上、ワイルドカード * ドットから始まる「隠しファイル(.env.gitignore.htaccess 等)」を展開対象外として無視 します。

【src/* vs src/. の決定的な違い】

1. cp -a src/* dst/ (NG:隠しファイルが漏れる)
   → シェル展開が隠しファイル(.env など)を除外するため、
     本番環境で重要な環境設定ファイルがコピーされず障害の原因に!

2. cp -a src/. dst/ (OK:隠しファイルも含めて完全コピー)
   → シェル展開に依存せず「ディレクトリ自身の中身」を渡すため、
     通常ファイルも隠しファイルも100%漏れなくコピーされる!

現代の開発現場では .env.dockerignore などドットファイルが中核を担っています。コピー漏れによる事故を防ぐため、 中身のコピーは src/* ではなく必ず src/. を使う ことを徹底してください。

-r と -a の違い(なぜバックアップには -a を使うべきなのか)

実務で最も重要なのが -r-a の違いの把握です。なぜバックアップやシステム移行で -r を使ってはいけないのか、その理由を解説します。

-r オプションの弱点(所有者・タイムスタンプが実行ユーザー・現在日時に変更される)

cp -r は単に「階層をたどってデータをコピーする」だけのオプションです。メタデータ(属性)を保持しないため、以下の副作用が生じます。

  • タイムスタンプの消失 :全ファイルの更新日時が「cp を実行した現在日時」に書き換わり、過去の更新履歴が分からなくなります。
  • 所有者の変更 :元の所有者(例: www-data, nginx)が、コマンドを実行したユーザーに書き換わります。
  • シンボリックリンクの実体化 :リンク先の実体データが重複してコピーされ、ディスク容量が無駄に消費されます。

-a(アーカイブモード)が保持する属性一覧(-p, -d, -r の完全統合)

これらをすべて解決するのが -a(archive)オプション です。-a は以下の3つの必須オプションを包括しています。

統合されるオプション機能の詳細実務上の役割
-r または -Rディレクトリの再帰的コピーサブディレクトリや深層ファイルをすべて複製する
-d(–no-dereference –preserve=links)シンボリックリンクを維持リンク先の実体を無駄にコピーせず、リンクのまま保持する
-p(–preserve=mode,ownership,timestamps)権限・所有者・更新日時の保持パーミッション(chmod)、所有者(chown)、タイムスタンプを維持

cp -a を使えば、 「階層構造」「リンク関係」「ファイルの権限・日時・所有者」 を完全な状態でクローンできます。

Webサーバー移行や設定ファイル退避で -a が必須となる理由

Webサーバーの設定ファイルやデータベース領域(/var/lib/mysql 等)を cp -r で退避すると、所有者が変わり、サービス再起動時に Permission denied で起動不能になります。

また、更新日時が変わると make などのビルドツールが全ファイルの再コンパイルを実行してしまう問題も生じます。 「本番環境での退避・バックアップは無条件に cp -a を選ぶ」 のが実務の鉄則です。

実務で必須の主要オプション完全ガイド

日々のコマンドライン操作で頻出する、覚えておくべき主要オプションと実務テクニックをまとめました。

-p: パーミッション・所有者・タイムスタンプの個別保持

-p(preserve)は、属性情報(パーミッション、所有者、タイムスタンプ)を個別に保持してコピーするオプションです。単一ファイルの退避で重宝します。

# 単一ファイルを属性保持してバックアップ
cp -p /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak

ディレクトリコピー時は -a-p が内包されているため、cp -a を使えば十分です。

-i: 上書き時の確認プロンプト表示

-i(interactive)は、コピー先に同名ファイルがある場合に上書きの可否を対話式で確認するオプションです。

$ cp -i file.txt backup/
cp: overwrite 'backup/file.txt'? 

y で上書き実行、n でスキップします。重要ファイルの誤消去を防ぐ安全対策として有効です。

-f および \cp: 強制上書き(alias cp=’cp -i’ の一時無効化テクニック)

実務でよくあるのが、 -f(force)を付けたのに上書き確認プロンプトが何回も表示される」 という現象です。

# -f を指定したのに確認が出て止まる!
$ cp -rf src/ dst/
cp: overwrite 'dst/file1.txt'? 
cp: overwrite 'dst/file2.txt'? 

この原因は、多くのLinux環境で alias cp='cp -i' というエイリアスが標準設定されている からです。シェルが cp を自動的に cp -i へ置き換えてしまうため、-f より -i が優先されてしまいます。

確認プロンプトをスキップして一括強制上書きするには、以下のテクニックを使います。

# 解決策1:先頭にバックスラッシュを付ける(最推奨)
\cp -rf src/ dst/

# 解決策2:フルパスでコマンドを直接実行する
/bin/cp -rf src/ dst/

最もおすすめなのが 「先頭にバックスラッシュを付ける \cp です。シェルのエイリアス展開を今回限りスキップして元のバイナリを実行するため、設定を変更することなくスムーズに強制上書きできます。

-v: コピー進捗・対象ファイルの詳細ログ表示

-v(verbose)は、コピーされたファイルを1行ずつターミナルに表示するオプションです。

$ cp -av src/. dst/
'src/./config.json' -> 'dst/config.json'
'src/./index.html' -> 'dst/index.html'
'src/./images/logo.png' -> 'dst/images/logo.png'

大量のファイルを処理する際、進行状況をリアルタイムで確認できるため、処理が止まっていないか確認するのに便利です。

-u: 更新日付が新しいファイルのみコピー(簡易増分同期)

-u(update)は、コピー先に同名ファイルがある場合、 コピー元の方が新しい場合のみ上書きする オプションです。

# 更新されたファイルや新規ファイルだけを差分コピー
cp -au src/. dst/

変更のない既存ファイルはスキップされるため、2回目以降のコピー処理を高速化できます。

–parents: 親ディレクトリの階層構造を維持してコピー

--parents は、コピー元の親ディレクトリのパス構造を維持したままコピー先に作成する強力なオプションです。

# /var/log/nginx/access.log を親パスごと /backup にコピー
cp --parents /var/log/nginx/access.log /backup/

上記を実行すると、コピー先には /backup/var/log/nginx/access.log という完全なディレクトリツリーが自動作成されます。複数ディレクトリの設定ファイルを集約バックアップする際に重宝します。

よくあるトラブルと解決策

ディレクトリコピーで現場でよく発生するトラブルと、その具体的な回避策を解説します。

シンボリックリンクが実体コピーされて容量が肥大化する問題(-d / -a の使い分け)

シンボリックリンクを含むディレクトリを cp -r でコピーすると、リンク先の実体データが重複してコピーされてしまい、ディスク容量を急速に圧迫するリスクがあります。

オプションシンボリックリンクの扱い実務での使い分け
-d または -aシンボリックリンクをそのままリンクとしてコピー(推奨)バックアップ、環境移行、容量節約
-L(–dereference)リンクをたどり参照先の実体データをコピー外部配布用アーカイブの作成など、リンクを解消したい場合

リンク構造をそのまま維持したい場合は、必ず cp -a を使用してください。

パーミッションエラー(Permission denied)とsudo実行時の注意点

システムディレクトリ(/etc/var/log 等)を一般ユーザーでコピーしようとすると、Permission denied エラーになります。

$ cp -a /etc/nginx/ /tmp/backup/
cp: cannot open '/etc/nginx/nginx.conf': Permission denied

この場合は sudo を付けて実行しますが、 コピー先ファイルの所有者が root になる点に注意が必要 です。

# sudo でコピー後、必要に応じて所有者を変更する
sudo cp -a /path/to/src/ /path/to/dst/
sudo chown -R www-data:www-data /path/to/dst/

大量データや定期同期なら「rsync」を使うべき理由

ローカル環境での日常的なディレクトリコピーは cp -a で十分ですが、 数GB以上の大容量データや、定期的なミラーリング、別サーバーへの転送 には rsync コマンドが適しています。

差分転送による圧倒的な速度差と中断再開

cp は毎回全データを読み書きしますが、rsync は変更のあった差分ブロックだけを転送するため、2回目以降の同期速度が劇的に向上します。

比較項目cp コマンド(cp -a)rsync コマンド(rsync -avz)
主な用途ローカルでの単発コピー・バックアップ大容量データ同期・定期ミラーリング・リモート転送
差分転送なし(毎回全ファイルを再コピー)あり(変更のあった差分ブロックのみ転送)
処理の中断・再開不可(最初からやり直し)可能(--partial で途中から再開可能)
不要ファイルの削除不可(手動削除が必要)可能(--delete による完全同期)
事前確認不可可能(--dry-run でシミュレーション)
SSHリモート転送不可(scp が必要)可能(標準でSSH暗号化通信に対応)
# rsync による高速なディレクトリ同期の例
rsync -avh --progress src/ dst/

単発のバックアップには手軽な cp -a、定期バックアップや大規模データには rsync と使い分けましょう。

まとめ

Linuxにおける cp コマンドでのディレクトリコピーについて解説しました。実務で安全に運用するための 最重要3大ポイント をまとめます。

  • バックアップは迷わず cp -a を使う: 単純コピー(-r)では所有者や日時が変わります。属性を完全保持する -a が本番運用の基本です。
  • 中身だけ展開するなら src/. を指定する: コピー先が存在する場合の二重入れ子を防ぎ、.env などの隠しファイルも漏れなく展開できます。
  • 上書き確認のスキップは \cp を活用する: エイリアス(cp -i)による確認連打は、先頭にバックスラッシュを付けることで一時的に無効化できます。

ディレクトリコピーをマスターしたら、以下の関連記事もあわせて活用し、Linuxの実務スキルをさらに高めていきましょう。

関連おすすめ記事

Bash玄

はじめまして!Bash玄です。

エンジニアとしてシステム運用に携わる中で、手作業の多さに限界を感じ、Bashスクリプトを活用して業務を効率化したのがきっかけで、この道に入りました。「手作業は負け」「スクリプトはシンプルに」をモットーに、誰でも実践できるBashスクリプトの書き方を発信しています。

このサイトでは、Bashの基礎から実践的なスクリプト作成まで、初心者でもわかりやすく解説しています。少しでも「Bashって便利だな」と思ってもらえたら嬉しいです!

# 好きなこと
- シンプルなコードを書くこと
- コマンドラインを快適にカスタマイズすること
- 自動化で時間を生み出すこと

# このサイトを読んでほしい人
- Bashに興味があるけど、何から始めればいいかわからない人
- 定型業務を自動化したい人
- 効率よくターミナルを使いこなしたい人

Bashの世界に一歩踏み出して、一緒に「Bash道」を極めていきましょう!

Bash玄をフォローする