web-dev-qa-db-ja.com

サブディレクトリのないディレクトリのみを見つける方法は?

Linuxに、ブランチの終わり(ここではそれらをリーフと呼びます)、つまりサブディレクトリのないディレクトリだけのディレクトリツリーを調べる方法はありますか?私は この質問 を見ましたが、適切に回答されていませんでした。

だから私がディレクトリツリーを持っているなら

root/
├── branch1
│   ├── branch11
│   │   └── branch111   *
│   └── branch12        *
└── branch2
    ├── branch21        *
    └── branch22
        └── branch221   *

ブランチの終わりにあるディレクトリ(*)、それでファイルの数ではなく、ディレクトリの数だけを見ますか?私の実際のケースでは、ファイルのあるものを探していますが、これらは、この例で見つけたい「葉」のサブセットです。

7
alle_meije

ファイルが含まれているリーフディレクトリのみを検索するには、参照されている質問 https://unix.stackexchange.com/a/203991/330217 または類似の質問 https:の回答を組み合わせることができます。 //stackoverflow.com/a/4269862/10622916 または https://serverfault.com/a/530328 with find 's ! -empty

find rootdir -type d -links 2 ! -empty

-links 2を使用したハードリンクのチェックは、従来のUNIXファイルシステムで機能するはずです。 -empty条件はPOSIX標準の一部ではありませんが、ほとんどのLinuxシステムで使用できるはずです。

KamilMaciorowskiのコメントによると、ディレクトリの従来のリンク数セマンティクスはBtrfsでは無効です。これは https://linux-btrfs.vger.kernel.narkive.com/oAoDX89D/btrfs-st-nlink-for-directories で確認されており、従来の例外としてMac OS HFS +について言及しています動作。これらのファイルシステムでは、リーフディレクトリをチェックするために別の方法が必要です。

11
Bodo

ネストされたfindを使用して、サブディレクトリの数を数えることができます:

find . -type d \
  \( -exec sh -c 'find "$1" -mindepth 1 -maxdepth 1 -type d -print0 | grep -cz "^" >/dev/null 2>&1' _ {} \; -o -print \)
4
pLumo

*/ファイル名展開パターンがディレクトリの名前ではないものに展開されると、現在のディレクトリには(非表示ではない)サブディレクトリがありません。

findの場合:

find root -type d -exec sh -c 'set -- "$1"/*/; [ ! -d "$1" ]' sh {} \; ! -empty -print

これは、パターンがシンボリックリンクを通過するため、リーフディレクトリ内のディレクトリへのシンボリックリンクをディレクトリとして扱うことに注意してください。

-empty述語は標準ではありませんが、しばしば実装されます。それがなければ、サブディレクトリを検出するのと同じようなことをするでしょう:

find root -type d \
    -exec sh -c 'set -- "$1"/*/; [ ! -d "$1" ]' sh {} \; \
    -exec sh -c 'set -- "$1"/*;  [   -e "$1" ]' sh {} \; -print

または、もう少し効率的に、

find root -type d -exec sh -c '
    dir=$1
    set -- "$dir"/*/
    [ -d "$1" ] && exit 1
    set -- "$dir"/*
    [ -e "$1" ]' sh {} \; -print

または、-links忘れていた述語( thanks Bodo ):

find root -type d \
    -links 2 \
    -exec sh -c 'set -- "$1"/*; [ -e "$1" ]' sh {} \; -print
4
Kusalananda

zshの場合:

leafdirs=(**/*(ND/Fl2))

一致するリーフディレクトリのリストを$leafdirs配列に設定します。

あなたはそれを1行に1つ印刷できます:

printf '%s\n' $leafdirs

または、次のようにループします:

for dir ($leafdirs) something with $dir

これは多かれ少なかれ @ BodoのGNU find answer の翻訳なので、実際のディレクトリエントリとしてext4.を実装する..などの従来のUnix風のファイルシステムでのみ機能します。

  • **/:任意のレベルのサブディレクトリ
  • (NF/Fl2):グロブ修飾子
  • N:その1つのグロブに対してnullglobを有効にします。一致がない場合でも失敗しません。代わりに、結果は空のリストになります
  • D:その1つのグロブに対してdotglobを有効にします。隠しディレクトリ内を調べ、隠しファイルをスキップしません
  • /:タイプディレクトリのみのファイルを選択(-type dなど)
  • F:selectfullディレクトリ:.および..以外の少なくとも1つのエントリを含むディレクトリ(GNU find! -emptyなど)
  • l2:リンク数が2のdirsのみ(-links 2など)。 1つは親ディレクトリのdirのエントリ用で、もう1つはその中の.用です。これらの..エントリのため、サブディレクトリはリンクカウントに1を追加します。
4