UNIXシェルスクリプト:ファイルを1つのディレクトリに再帰的に移動する方法


8

次のようなディレクトリ構造に配置された多数の小さなファイルfがあります。

/A/B/C/f

Aレベルには11個のディレクトリがあり、それぞれBレベルに約100個のディレクトリがあり、それぞれCレベルに約30個のディレクトリがあり、それぞれに1つのファイルfがあります。

すべてのファイルを1レベル上に移動するにはどうすればよいですか?たとえば、このファイルのセットを考えると...

/ A / B / C / f1
/ A / B / C / f2
/ A / B / C / f3
/ A / B / C / f4

ディレクトリ/A/B/にf1からf4までの4つのファイルを含めたい。ディレクトリCを削除する必要はありません。

私は、これが問題の解決策であることを望ん可能性が関与していますfindxargswhatnot。何か案は?

乾杯、

ジェームズ

回答:


9

GNU find(Linuxで見つかる)や、以下をサポートする他のfindを使用すると、非常に簡単-execdirです。

find A -type f -execdir mv -i {} .. \;

標準でfind

find A -type f -exec sh -c 'mv -i "$1" "${1%/*}/.."' sh {} \;

zshの場合:

zmv -Q -o-i 'A/(**/)*/(*)(.)' 'A/$1$2'

ディレクトリ構造のネストレベルが常に同じである場合、再帰的な走査は必要ありません(ただし、最初に空のディレクトリを削除します)。

for x in */*; do; echo mv -i "$x"/*/* "$x"/..; done

1

そのファイルセットの場合、これは次のようになります。

$ cd /A/B/C/
$ mv ./* ../

しかし、私はあなたの問題が少し複雑になることを期待しています...私はこれに答えることができません...私はあなたのdir構造がどのようになっているのかよくわかりません...明確にできますか?


これは明らかに1つのディレクトリ/ A / B / Cで機能しますが、このようなディレクトリが約30000あるので、階層の最上位で実行して一度に処理できるコマンドを望んでいます。
jl6

1
@ jl6わかりました。ファイルだけを移動する必要がありますか?したがって、CはBに移動する必要はなく、BはAに移動する必要はありません...
スタックオーバーフローは

正解です。操作は、階層の最下位レベルのファイルに対してのみ実行する必要があり、1レベル上にのみ移動する必要があります。
jl6

0

私の最初の推測は

$ find A -type f -exec mv {} .. \;  

指定しない限り、問題ありません-depth。私は試したことがないので、コミットする前にまずテストしてください。


同じ名前の2つのファイルがある場合、findは何をしますか?

Findは問題ではなく、組み込みの "mv"コマンドが問題です。重複名はmvが名前を変更する予定がないため、適切ではありません。上書きされます。mv -iを使用して少し手助けすることもできますが、30,000以上のファイルがあると苦痛になる可能性があります。状況をこれほどまでに制御する必要がある場合は、スクリプト言語で記述されたプログラムが必要になることがあります(私の選択はpython、YMMVです)。
布袋

私はmvで「バックアップ」を使用していませんが、manページを見ると、重複名の問題に対する解決策であると思われます。「A -type f -exec mv --backup numberd {} .. \;を見つけます」動作する可能性があります(--backupではダブルダッシュです)
hotei

与えられたfindの例は、最下位レベルのすべてのファイルをAの1つ上のディレクトリに移動します( ".."は、find / mvに渡される前にシェルによって解釈されていると思います)。階層の1つ上のレベルにファイルを移動する必要があります。さらに1つの明確化:ファイルが1レベル上にしか移動しない限り、ファイル名の重複はありません。
jl6

@hotei:mvコマンドは現在のディレクトリで実行されるため、期待どおりに..動作しません。解決策については私の答えを参照しください。@ jl6:シェルはとは関係がなく..、カーネルによって処理されます。
Gilles「SO-邪悪なことをやめよ」

0

リーフディレクトリにあるファイルのみを移動する場合(つまり、サブディレクトリが含まれ/A/B/fileている/A場合Bは移動したくない場合)は、次の2つの方法があります。

どちらもこれが必要です

leaf ()
{
    find $1 -depth -type d | sed 'h; :b; $b; N; /^\(.*\)\/.*\n\1$/ { g; bb }; $ {x; b}; P; D'
}
shopt -s nullglob

これは機能します:

leaf A | while read -r dir
do
    for file in "$dir"/*
    do
        parent=${dir%/*}
        if [[ -e "$parent/${file##*/}" ]]
        then
            echo "not moved: $file"
        else
            mv "$file" "$parent"
        fi
    done
done

これは高速になりますが、空のソースディレクトリは好きではありません。

leaf A | while read -r dir
do
    mv -n "${dir}"/* "${dir%/*}"
    remains=$(echo "$dir"/*)
    [[ -n "$remains" ]] && echo "not moved: $remains"
done
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.