web-dev-qa-db-ja.com

ネストされたディレクトリをフラット化する

これはおそらく非常に単純ですが、わかりません。私はこのようなディレクトリ構造を持っています(dir2はdir1内にあります):

/dir1
    /dir2 
       |
        --- file1
       |
        --- file2

Dir2でなくdir1のfile1とfile2を取得するように、このダイレクタ構造を「フラット化」する最良の方法は何ですか。

78
turtle

GNU findおよびGNU mv

_find /dir1 -mindepth 2 -type f -exec mv -t /dir1 -i '{}' +
_

基本的に、そのfindがディレクトリツリー全体を通過し、最上位ディレクトリ(_-type f_)にない各ファイル(_-mindepth 2_)に対して動作する方法は、mvを実行して移動します必要なディレクトリに(_-exec mv … +_)。 mvの_-t_引数を使用すると、最初に宛先ディレクトリを指定できます。これは、_+_形式の_-exec_がすべてのソースの場所をコマンドの最後に配置するために必要です。 _-i_は、重複を上書きする前にmvを尋ねます。質問せずに_-f_に置き換えることができます(要求しないか上書きしない場合は_-n_)。

Stephane Chazelasが指摘するように、上記はGNUツール(Linuxでは標準ですが、他のほとんどのシステムではありません)でのみ機能します。以下は多少遅くなります(mvを複数回呼び出すため)より普遍的:

_find /dir1 -mindepth 2 -type f -exec mv -i '{}' /dir1 ';'
_
79
derobert

Zshの場合:

mv dir1/*/**/*(.D) dir1

**/はサブディレクトリを再帰的にトラバースします。 glob qualifier.は通常のファイルのみに一致し、Dはドットファイルが含まれることを保証します(デフォルトでは、名前が.で始まるファイルはワイルドカード一致から除外されます)。空になったディレクトリを後でクリーンアップするには、rmdir dir1/**/*(/Dod)を実行します。/はディレクトリに制限され、odは最初に一致深度を順序付けて、dir1/dir2/dir3の前にdir1/dir2を削除します。

ファイル名の全長が非常に長い場合、コマンドラインの長さが制限されることがあります。 Zshにはmvrmdirのビルトインがあり、これらの制限の影響を受けません。これらを有効にするには、zmodload zsh/filesを実行します。

POSIXツールのみ:

find dir1 -type f -exec mv {} dir1 \;
find dir1 -depth -exec rmdir {} \;

または(ファイルごとに個別のプロセスを実行する必要がないため、より高速です)

find dir1 -type f -exec sh -c 'mv "$@" dir1' _ {} +
find dir1 -depth -exec rmdir {} +

tarとZipはどちらも、ディレクトリ構造を組み込んで削除する機能を備えているため、ネストされたディレクトリをすばやくフラット化できました。

tar -cvf all.tar *

次にall.tarを新しい場所に移動します。

tar -xvf all.tar --strip=4

3
John

これを試してください:

cp /dir1/dir2/file{1,2} /another/place

または、サブディレクトリのfile[0-9]*に一致するファイルごとに:

cp /dir1/dir2/file[0-9]* /another/place

参照 http://mywiki.wooledge.org/glob

3
Gilles Quenot

一緒に使用できる2つの関数を作成しました。そのために、-maxdepth $VALパラメータを追加して、ディレクトリレベルを制限できます。

# This scripts flattens the file directory
# Run this script with a folder as parameter:
# $ path/to/script path/to/folder

#!/bin/bash

rmEmptyDirs(){
    local DIR="$1"
    for dir in "$DIR"/*/
    do
        [ -d "${dir}" ] || continue # if not a directory, skip
        dir=${dir%*/}
        if [ "$(ls -A "$dir")" ]; then
            rmEmptyDirs "$dir"
        else
            rmdir "$dir"
        fi
    done
    if [ "$(ls -A "$DIR")" ]; then
        rmEmptyDirs "$DIR"
    fi
}

flattenDir(){
    local DIR="$1"
    find "$DIR" -mindepth 2 -type f -exec mv -i '{}' "$DIR" ';'
}

read -p "Do you wish to flatten folder: ${1}? " -n 1 -r
echo    # (optional) move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]
then
    flattenDir "$1" &
    rmEmptyDirs "$1" &
    echo "Done";
fi
2
Bruno

同じ名前のファイルを含むディレクトリをフラット化するための使用例があったため、この質問に対する一般的な回答を拡張します。

dir1/
├── dir2
│   └── file
└── dir3
    └── file

この場合、mvに渡された-i--interactive)オプションは、ディレクトリ構造をフラット化し、名前の競合を処理するための望ましい結果をもたらしません。したがって、単に--backup=tに置き換えられます(--backup=numberedと同等)。 https://www.gnu.org/software/coreutils/manual/coreutils.html#Backup-options にある-b--backup)オプションの詳細なドキュメント。

その結果:

find dir1/ -mindepth 2 -type f -exec mv -t dir1/ --backup=t '{}' +

収量:

dir1/
├── dir2
├── dir3
├── file
└── file.~1~
1
Yann Eves