web-dev-qa-db-ja.com

検索を使用してファイルを移動するときにディレクトリ構造を保持する

定義されている古いファイルをソースディレクトリから宛先ディレクトリに移動する次のスクリプトを作成しました。完全に機能しています。

#!/bin/bash

echo "Enter Your Source Directory"
read soure

echo "Enter Your Destination Directory"
read destination 

echo "Enter Days"
read days



 find "$soure" -type f -mtime "-$days" -exec mv {} "$destination" \;

  echo "Files which were $days Days old moved from $soure to $destination"

このスクリプトはファイルを移動します。また、ソースサブディレクトリのファイルも移動しますが、サブディレクトリを宛先ディレクトリに作成しません。この追加機能を実装したい。

例で

/home/ketan     : source directory

/home/ketan/hex : source subdirectory

/home/maxi      : destination directory

このスクリプトを実行すると、hexのファイルもmaxiディレクトリに移動しますが、同じhexをmaxiディレクトリに作成し、そのファイルを同じhexに移動する必要があります。

23
Ketan Patel

mv /home/ketan/hex/foo /home/maxiを実行する代わりに、findによって生成されたパスに基づいてターゲットディレクトリを変更する必要があります。これは、最初にソースディレクトリに移動してfind .を実行すると簡単です。これで、findによって生成された各アイテムの宛先ディレクトリを単に追加できます。 find … -execコマンドでシェルを実行して連結を実行し、必要に応じてターゲットディレクトリを作成する必要があります。

destination=$(cd -- "$destination" && pwd) # make it an absolute path
cd -- "$source" &&
find . -type f -mtime "-$days" -exec sh -c '
  mkdir -p "$0/${1%/*}"
  mv "$1" "$0/$1"
' "$destination" {} \;

$destinationに特殊文字が含まれている場合に、引用の問題を回避するために、シェルスクリプト内でそれを置き換えることはできません。それを環境にエクスポートして内部シェルに到達させるか、または引数として渡すことができます(それが私が行ったことです)。 sh呼び出しをグループ化することで、実行時間を少し節約できます。

destination=$(cd -- "$destination" && pwd) # make it an absolute path
cd -- "$source" &&
find . -type f -mtime "-$days" -exec sh -c '
  for x do
    mkdir -p "$0/${x%/*}"
    mv "$x" "$0/$x"
  done
' "$destination" {} +

または、zshでは、 zmv function を使用し、.およびmglob qualifiers を使用してのみ一致させることができます正しい日付範囲の通常のファイル。必要に応じて、最初にターゲットディレクトリを作成する代替のmv関数を渡す必要があります。

autoload -U zmv
mkdir_mv () {
  mkdir -p -- $3:h
  mv -- $2 $3
}
zmv -Qw -p mkdir_mv $source/'**/*(.m-'$days')' '$destination/$1$2'

findが指定されたことは知っていますが、これはrsyncの仕事のように思えます。

私は最もよく以下を使用します:

rsync -axuv --delete-after --progress Source/ Target/

特定のファイルタイプ( example )のファイルのみを移動する場合の良い例を次に示します。

rsync -rv --include '*/' --include '*.js' --exclude '*' --Prune-empty-dirs Source/ Target/
17
ryanjdillon

それは効率的ではありませんが、私の意見では、ファイルをコピーして後で削除するだけであれば、コードは読みやすく、理解しやすくなります。

find /original/file/path/* -mtime +7 -exec cp {} /new/file/path/ \;
find /original/file/path/* -mtime +7 -exec rm -rf {} \;

通知:自動操作のために@MVによって発見された欠陥:

2つの別々の操作を使用するのは危険です。コピー操作の実行中に7日以上経過した一部のファイルはコピーされませんが、削除操作によって削除されます。これは問題ではないかもしれませんが、手動で行われている場合は自動化されたスクリプトの場合、データが失われる可能性があります

3
Elliott Post

find(1)の2つのインスタンスを使用してそれを行うことができます

常にcpio(1)があります

(cd "$soure" && find … | cpio -pdVmu "$destination")

Cpioの引数を確認してください。私があげたもの

3
Mark Lamourine

これを行うには、findによって返されたファイルの絶対パスを宛先パスに追加します。

find "$soure" -type f -mtime "-$days" -print0 | xargs -0 -I {} sh -c '
    file="{}"
    destination="'"$destination"'"
    mkdir -p "$destination/${file%/*}"
    mv "$file" "$destination/$file"'
2
Chris Down

また、ファイル名に特殊文字が含まれている場合、ファイル名の影響を受けません(移動ではなくコピーを実行することにより、最も速く、ストレージ領域を消費しません)。

export destination
find "$soure" -type f "-$days" -print0 | xargs -0 -n 10 bash -c '
for file in "$@"; do
  echo -n "Moving $file to $destination/"`dirname "$file"`" ... "
  mkdir -p "$destination"/`dirname "$file"`
  \mv -f "$file" "$destination"/`dirname "$file"`/ || echo " fail !" && echo "done."
done'

または、「並列」コマンドを使用して、マルチCPUで多数のファイルを同時に移動します。

echo "Move oldest $days files from $soure to $destination in parallel (each 10 files by "`parallel --number-of-cores`" jobs):"
function move_files {
  for file in "$@"; do
    echo -n "Moving $file to $destination/"`dirname "$file"`" ... "
    mkdir -p "$destination"/`dirname "$file"`
    \mv -f "$file" "$destination"/`dirname "$file"`/ || echo " fail !" && echo "done."
  done
}
export -f move_files
export destination
find "$soure" -type f "-$days" -print0 | parallel -0 -n 10 move_files

PS:タイプミスがあります。「酸味」は「ソース」である必要があります。変数名はそのままにしておきました。

1
Bogdan Velcea

これに対する本当に簡単な解決策はないようで、私はそれを頻繁に必要とするので、私はLinux用のこのオープンソースユーティリティを作成しました(Pythonが必要です): https://github.com/benapetr/smv

それを使用して必要なものを達成する方法はいくつかありますが、おそらく最も簡単な方法は次のようになります。

 # -vf = verbose + force (doesn't stop on errors)
smv -vf `find some_folder -type f -mtime "-$days"` target_folder

さらに、それをドライモードで実行して、何もしないで何をするかを印刷することができます

smv -rvf `find some_folder -type f -mtime "-$days"` target_folder

または、ファイルのリストが長すぎて引数行に収まらない場合、pythonをすべてのファイルに対して実行してもかまいません。

find "$soure" -type f -mtime "-$days" -exec smv {} "$destination" \;
0
Petr

これはエレガントではありませんが、ファイルの数/サイズがあまり大きくない場合は簡単です

ファイルをZipアーカイブに圧縮してから、-jオプションなしで宛先で解凍します。デフォルトでは、Zipは相対ディレクトリ構造を作成します。

0
Chris Johnson

この方法を試してください:

IFS=$'\n'
for f in `find "$soure" -type f -mtime "-$days"`;
do
  mkdir -p "$destination"/`dirname $f`;
  mv $f "$destination"/`dirname $f`;
done
0
Alessio