他の(サブ)ファイルとディレクトリのセットを除いて、1セットのファイルとディレクトリを移動する方法

他の(サブ)ファイルとディレクトリのセットを除いて、1セットのファイルとディレクトリを移動する方法

2つのbash配列があり、1つ(名前toMove)にはファイルとディレクトリへのパスが含まれています。移動(コピーではない)他の場所には、exclude移動から除外するファイルとディレクトリへのパスを含む別のファイル()が含まれています。

toMove=(
    tree/subtree1
    tree/subtree2
    tree/subtree3/leaffile3
)
exclude=(
    tree/subtree1/leafdir1
    tree/subtree2/leaffile2
)

# move code ?

テストディレクトリ構造は次のとおりです。

mkdir -p tree/{subtree1/leafdir1,subtree2/leafdir2,subtree3/leafdir3}
touch tree/{subtree1/leaffile1,subtree2/leaffile2,subtree3/leaffile3}
tree tree
    tree
    ├── subtree1
    │   ├── leafdir1
    │   └── leaffile1
    ├── subtree2
    │   ├── leafdir2
    │   └── leaffile2
    └── subtree3
        ├── leafdir3
        └── leaffile3

取締役後の予想事項:

tree tree
    tree
    ├── subtree1
    │   └── leafdir1
    ├── subtree2
    │   └── leaffile2
    └── subtree3
        └── leafdir3

tree destination
    destination
    ├── subtree1
    │   └── leaffile1
    ├── subtree2
    │   └── leafdir2
    └── subtree3
        └── leaffile3

(これと組み合わせて)私が望むものを正確に実行するrsyncオプションがありますが、残念ながら--exclude-from=--remove-source-filesrsync ファイルをコピーしますが、私には必要です。移動するそれら(パフォーマンス上の理由で)同じファイルシステムにある場合。

私が考えた解決策は、findを使用してtoMove配列のすべてのパス(ディレクトリの内容を含む)のリストを取得し、このリストを繰り返し、除外配列のパスで始まるすべてのパスをフィルタリングすることです。これが問題を解決する正しい方法ですか、それともこの問題を解決するより簡単でエレガントな方法(いくつかの標準ユーティリティを使用)がありますか?

修正する:

問題は、初めて見ると思うほど些細ではなく、質問の表現も悪いことがわかりました。 「他のパスのパスをフィルタリングし、影響を受けていないツリー全体を保護すること」に関するものでなければなりません。

私は空のリーフディレクトリを保持しませんが、以下の解決策で終わり、すべてのファイルを移動し、場合によっては十分な場合に親ツリーのみを移動するという2番目の欠点があります。

...
comm \
    -23 \
    <(find "${toMove[@]}"  ! -type d 2>/dev/null | sort -u || true) \
    <(find "${exclude[@]}" ! -type d 2>/dev/null | sort -u || true) |
        parallel -j "$(nproc)" -- moveFile {}
...

(moveFileは、移動を処理するために使用されるエクスポートされたbash関数です。スクリプトがファイルシステムで実行されていないときに作業を高速化するには(数回)、パラレルを使用します。

Stefan Chazerasの答えzsh私が以前に知らなかったことをたくさん教えてくれました。

答え1

すべてのソースとターゲットが同じファイルシステムにある場合、moveはaであり、デフォルトではrename()+と同じです。link()unlink()

標準(広範囲ではありませんが)paxコマンド、(以前の標準)コマンド、およびcpioGNU実装with withは、ディレクトリ構造をシンボリックリンクにコピーできます(まだ再生する必要があるディレクトリには適用されません)。 (/)と(/オプション)を使用してGNU実装を実行できます。cp-alzshbashcpio-0--nullrm-d--dir

#! /bin/zsh -
src=tree
dst=destination
toMove=(
  subtree1
  subtree2
  subtree3/leaffile3
)
exclude=(
  subtree1/leafdir1
  subtree2/leaffile2
)

set -o extendedglob
autoload zargs

src=$src:P
dst=$dst:P

cd -- $src || exit

allToMove=( $^toMove{,/**/*}~(${(~j[|])exclude})(|/*)(ND) )
print -rNC1 -- $allToMove |
  cpio --pass-through --null --link --make-directories -- $dst &&
  zargs -r -- ${(Oa)allToMove} -- rm -d --

名前を変更したばかりのディレクトリを含む、すべてのターゲットディレクトリを再生成し、そこにあるすべてのファイルをリンクするので、それでも改善することができますが、少なくともディレクトリ以外のファイルのデータはコピーされませんでした。

--make-directories移動するリストに含まれていないため、作成されたディレクトリ(ディレクトリなど)には、subtree3ソースからそのメタデータ(所有権、権限など)がコピーされません。

ディレクトリが空でないため、ディレクトリを削除できないという警告が表示されます。

使用されるzsh機能のいくつかは次のとおりです。

  • $var:P$var標準を使用するのと同じように、保存されたファイルの絶対標準パスに拡張されます(realpath()入力時に必要なので、相対パスはもはや同じファイルを参照しません)。$dstcd$srcdestination
  • $^array/x/などの配列を展開しますrc。たとえば、要素が含まれている場合は代わりになります。fish$arrayABAx BxA Bx
  • A{x,y}cshに似た中括弧拡張であり、 としても拡張されますAxAy
  • **/すべてのレベルのサブディレクトリと一致します。
  • in は、glob~patternここで除外を適用するために使用される~and-not/Exception 演算子です。extendedglob
  • (${(~j[|])exclude})(|/*)j配列要素(|パラメータ拡張フラグのためにリテラルではなくグローバル演算子として扱われます)を追加し、要素またはその中のファイルと一致するように構成された除外パターンとして追加します。|~(|/*)
  • (ND)glob修飾子はNullglobとDotglobをこれらのglobに適用するため、隠しファイルが含まれており、globが一致しなくてもエラーは生成されません。
  • print -rNC1ULで区切られた列に対応するパラメータrawを印刷します。1 CN
  • ${(Oa)array}a逆順に拡張して、O葉がある枝の前に葉を取り除きます。
  • zargs避けられるものはありますか?パラメータリストが長すぎます。削除するファイルのリストが多すぎると、エラーが表示されます。

関連情報