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


22

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

#!/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

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

回答:


13

実行する代わりにmv /home/ketan/hex/foo /home/maxi、によって生成されたパスに基づいてターゲットディレクトリを変更する必要がありますfind。これは、最初にソースディレクトリに移動して実行すると簡単ですfind .。これで、によって生成された各アイテムに宛先ディレクトリを単に追加できますfindfind … -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関数.およびm glob修飾子を使用して、適切な日付範囲の通常のファイルのみを一致させることができます。必要に応じてmv、最初にターゲットディレクトリを作成する代替関数を渡す必要があります。

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

for x do;そこに行方不明があります:)。また、私はあなたが何を達成したいのか分かりません$0が、私はそれがsh:) だと確信しています。
ミチャウゴルニー14年

@MichałGórny for x; doは技術的にPOSIXに準拠していません(文法を確認してください)が、最新のシェルではとの両方が許可されfor x doていfor x; doます。古いBourneシェルのいくつかは苦手for x; doでした。現代のシェルでは、とsh -c '…' arg0 arg1 arg2 arg3arg0なると$0arg1なると$1、あなたがしたい場合など$0であることをsh、次のように記述する必要がありますsh -c '…' sh arg1 arg2 arg3。繰り返しますが、一部のBourneシェルの動作は異なりますが、POSIXではこれが指定されています。
ジル「SO-悪であるのをやめる」

pushdcd、現在の環境への侵入が少ないため、より良い選択のようです。
jpmc26

16

私は知っていましたfindが、これはの仕事のように聞こえrsyncます。

私はほとんどの場合、次のものを使用します。

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

特定のファイルタイプのファイルのみを移動したい場合の良い例です():

rsync -rv --include '*/' --include '*.js' --exclude '*' --prune-empty-dirs Source/ Target/

他のソリューションよりもはるかにクリーン:)
ギヨーム

2
これ--remove-source-filesは有用であり、ファイルをコピーする代わりに効果的に移動させることがわかりました。これはrsyncの素晴らしい使用法です。
トム

3

find(1)の2つのインスタンスを使用して実行できます

常にcpio(1)があります

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

cpioの引数を確認してください。私が与えたもの


1
これは、空白を含むファイル名で壊れます。
クリスダウン

1
これにより、ファイルを移動する代わりにコピーします。
ジル「SO-悪であるのをやめる」

完璧な答えではないかもしれませんが、パスを保持したファイルを多かれ少なかれ簡単に移動するのに役立ちました(ファイルをコピーしてから削除するのに十分なスペースがあります)。賛成
AhHatem

3

それほど効率的ではありませんが、ファイルをコピーして後で削除すれば、コードは読みやすく、理解しやすいと思います。

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

通知:@MVによって自動化された操作に関する欠陥が発見されました。

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


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

2
この問題の簡単な解決策は、findを1回実行し、リストをテキストファイルとして保存し、xargsを2回使用してコピーしてから削除することです。
デビッドM.パールマン

1

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

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

クリスは今、家全体をマキシに移動している
ケタンパテル

@ K.KPatelいいえ、そうではありません。単にディレクトリ構造を保持するだけです。
クリスダウン

$destination特殊文字が含まれている場合、これは破損します。これは、内部シェルで展開されるためです。もしかしてdestination='\'"$destination"\''?それはまだ壊れてい'ます。また、これによりの/home/maxi/home/ketan/hex/foo代わりになどのファイルが作成されます/home/maxi/hex/foo
ジル 'SO-悪であるのをやめる'

0

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

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:タイプミスがあります。「ソース」は「ソース」でなければなりません。変数名を保持しました。


0

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

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


0

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

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

0

これには本当に簡単な解決策はないようで、非常に頻繁に必要なので、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" \;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.