web-dev-qa-db-ja.com

BASH-mvを使用するときにディレクトリをマージする

BASHを使い始めたばかりなので、ディレクトリのすべての内容を別のディレクトリに移動できます。しかし、問題は、ディレクトリをマージすることを期待していたことです。このスクリプトを実行するためにcronジョブを使用しています。

#!/bin/bash

shopt -s dotglob nullglob
mv mv_schedule/* public_html/

ソース:/mv_schedule/(フォルダー/ファイルを含む)

/files/
4.html
5.html
/assets/
sitemap.xml

宛先:/public_html/(以前のフォルダーは既に存在します)

/files/
1.html
2.html
3.html
/assets/
sitemap.xml

だから私が欲しいのは、/mv_schedule/内のそれらのディレクトリからのすべての新しいファイルを/public_html/内のファイルとマージし、そこにすでに存在するファイルを次のファイルに置き換えることです/mv_schedule/から。

現在のスクリプトでこれを試してみると、現在、このエラーが電子メールで返されています。

mv: cannot move `mv_schedule/assets' to `public_html/assets': Directory not empty

どうすればこれを修正できますか?

5
The Bash

エラーメッセージが示しているのは、アセットディレクトリ内にファイルがあるため、アセットディレクトリを移動できないことです。 mvディレクトリとは、コピーを作成してから消去することを意味しますが、まだいくつかのファイルが含まれているため、削除できません。つまり、アセット内のファイル、およびアセット内のサブディレクトリとサブディレクトリ内のファイル(存在する場合)もmvする必要があります。ただし、発行されたmvコマンドは、mv_schedule内のサブディレクトリ(アセットなど)内に存在するものではなく、mv_scheduleディレクトリ内に存在するもののみをコピー(および消去)できます。

必要なのは、ディレクトリツリーを最後の一葉まで降ろしてコピーするコマンドです。これは、rm、chmod、chownの-rまたは-Rオプションのようなものです。ただし、mvにはそのようなオプションがないため、コマンドfindを使用する必要があります。このコマンドは、指定したルートからディレクトリツリー全体を下に移動し、指定したアクションを実行します。あなたの場合、適切なコマンドは次のとおりです。

SOURCE_DIR=$1
TARGET_DIR=$2
find $SOURCE_DIR -name '*' -type f -exec mv -f {} $TARGET_DIR \;

これにより、すべてのファイルが1つのターゲットディレクトリに積み上げられます。ソースディレクトリとターゲットディレクトリを入力行パラメータとしてbashスクリプトに渡したと想定しています。 -fオプションは、上書きの場合に確認を求めないようにします。これを-n(上書きしない)または-i(上書きする前に確認する)に変更できます。

代わりにディレクトリ構造を保持したい場合は、コマンドcpにはディレクトリツリーを下降する機能があることに注意してください。このコマンドにはツリーを下降する機能もあるため、このコマンドの後にrmを使用できます。考えられるコマンドのセットは次のとおりです。

SOURCE_DIR=$1
TARGET_DIR=$2
cp -a $SOURCE_DIR $TARGET_DIR
rm -rf $SOURCE_DIR

Cpの-aオプションに注意してください。タイムスタンプと所有権を保持します。そのようなことを気にしない場合は、代わりに-Rを使用できます。

6
MariusMatutiae

Unixセクションにはもっと一般的な この問題の議論 があります。

cpコマンドの-lオプションを使用できます。これにより、完全なファイルではなく、同じファイルシステム上に ハードリンク のファイルが作成されます。データコピー。次のコマンドは、フォルダsource/folderを親フォルダ(destination)にコピーします。このフォルダには、folderという名前のディレクトリがすでに含まれています。

cp -rl source/folder destination
rm -r source/folder

ノート:

  • 必要に応じて、-P--no-dereference-シンボリックリンクを逆参照しない)または-a--archive-すべてのメタデータを保持し、-Pオプションも含む)を使用することもできます。
  • 2つの「I/O」ステップが含まれますが、これらのステップは、ゼロの「データ」転送を含む比較的単純なメタデータ操作です。したがって、この方法は、cp(sans -l)またはrsyncベースのソリューションよりもはるかに高速です。
  • ソースフォルダと宛先フォルダが異なるファイルシステム上にある場合、これは機能しません
3
palswim

Palswimからの優れた回答。 (また、自分自身を賛成するために必要な最後の2つのポイントに対する賛成に感謝します!)

Cp -rl; rmは問題を解決し、最小限のI/Oで解決します(cpとrsyncはばかげてその点で高価です)。

これには2つの小さな問題があります。

  • 100%アトミックではありません-2つのI/O操作が必要です(--remove-source-filesは実際には別のI/O操作を必要とするため、これはrsyncメソッドにも当てはまります)。
  • ソースと宛先が異なるファイルシステム上にある場合は機能しません
1
zaTricky