Linux 検索-type d: ./dir1 なしで ./dir1/dir2 などの最も深いディレクトリをリストするにはどうすればよいですか?

Linux 検索-type d: ./dir1 なしで ./dir1/dir2 などの最も深いディレクトリをリストするにはどうすればよいですか?
find . -type d

通常は表示されます

./dir1
./dir1/dir2
./dir3
./dir3/dir4
./dir3/dir4/dir5
...

私はただ

./dir1/dir2
./dir3/dir4/dir5

、親 ./dir1 なし、...

つまり、サブディレクトリが含まれないディレクトリのみを表示したいのです。

何か案が?

編集:-links 2通常のLinux環境では動作することがわかりました。

docker run -it --rm ubuntu:bionic find /etc -type d -links 2

完璧に動作します。

ただし、ディレクトリ (MacOS または Windows から) を Docker コンテナーにマウントすると、状況が変わり、機能しなくなります。次の操作を試してください。

docker run -it --rm -v /etc:/etc_of_host ubuntu:bionic find /etc_of_host -type d -links 2

答え1

zsh

has_subdirs() () (( $# )) ${1-$REPLY}/*(ND/Y1)
print -rC1 -- **/*(ND/^+has_subdirs)

または、仲介機能を使わずに直接:

print -rC1 -- **/*(ND/^e['()(($#)) $REPLY/*(ND/Y1)'])

それは:

  • fn() some-commandBourne シェルやほとんどの Bourne 系シェルと同様に、 を本体とするfn関数を定義します。some-command
  • () { code } args位置パラメータとして実行さcodeれる匿名関数。args
  • (( $# ))ここで、その無名関数の本体は、次のように解決される算術式である。真実$#(引数の数)がゼロ以外の場合。
  • ${param-default}(Bourne シェルより)は$param、パラメータが設定されている場合は に展開され、defaultそうでない場合は に展開されます。ここで を使用すると、関数を直接 ( として) 呼び出すことも、以下のようにグロブ修飾子関数として${1-$REPLY}呼び出すこともできます。has_subdirs mydir
  • $dir/*(ND/Y1)は、隠しファイルも含め$dir( )内のディレクトリ タイプのファイルに展開され( otglob)、一致しない場合はエラーになりません ( ullglob)。ただし、 の場合は、最初の一致で停止します。したがって、匿名関数 (および) は、ディレクトリに少なくとも 1 つのサブディレクトリが含まれている場合に true を返します。/DNY1has_subdirs
  • print -rC1引数rawを1 C列に出力します
  • ^+has_subdirs関数が true を返さないファイルに制限しますhas_subdirs( ^)。

シェルを使用する必要がある場合はbash、次のようにします。

zsh << 'EOF'
  print -rC1 -- **/*(ND/^e['()(($#)) $REPLY/*(ND/Y1)'])
EOF

または、結果を bash 配列に保存するには、次のようにします (bash-4.4 以降が必要です)。

readarray -td '' array < <(zsh << 'EOF'
  print -rNC1 -- **/*(ND/^e['()(($#)) $REPLY/*(ND/Y1)'])
EOF
)

(任意のパスを保存できるように NUL で区切られたレコードを使用します)。

を持っていなくてzshも、 を持っている場合はperl、次のようにします。

find . -depth -type d -print0 |
  perl -l -0ne 'print if rindex $prev, "$_/", 0; $prev = $_'

あるいはGNUをお持ちの場合awk:

find . -depth -type d -print0 |
  gawk -v 'RS=\0' 'index(prev, $0"/") != 1; {prev = $0}'

それらはfindディレクトリを印刷します深さ優先(ブランチの前に残ります)、perl/ を取得してawk、見つからないレコードを出力し、その後に/前のレコードの先頭を出力します。

-l繰り返しになりますが、ファイルを bash 4.4 以降の配列に保存するには、 の後に を-0移動するperlか を追加して、-v 'ORS=\0'出力時に NUL 区切りのレコードに切り替える必要がありますgawk

readarray -td array < <(
  find . -depth -type d -print0 |
    perl -0lne 'print if rindex $prev, "$_/", 0; $prev = $_'
)
readarray -td array < <(
  find . -depth -type d -print0 |
    gawk -v 'RS=\0' -v 'ORS=\0' 'index(prev, $0"/") != 1; {prev = $0}'
)

一部のシステムおよびファイル システム (Linux ベースのシステム上のファイル システムなど) では、サブディレクトリを持つディレクトリのリンク数が 2 より大きい ( 、および ごとに追加のリンク)ext4という事実に依存できる場合があります。dirdir/.dir/subdir/..

print -rC1 -- **/*(ND/l-3)

またはfind任意のシェルで使用します:

find . -type d -links -3

btrfsこれは、たとえばディレクトリのリンク数が常に 1 である(いくつかの Linux ディストリビューションでデフォルトのファイルシステムとして置き換えられている) 場合には機能しないext4ため、一般的な解決策としてはお勧めしません。

overlayfsユニオンファイルシステムの場合も同様です。合併したディレクトリには、サブディレクトリが含まれているかどうかに関係なく、リンク カウントが 1 になります。

ただし、使用可能な場合は、ディレクトリへの読み取りアクセスを必要としない (親への検索アクセスのみを必要とする) サブディレクトリを手動でカウントするソリューションよりも利点があります。

答え2

あなたが探しているのは次のものです:

find . -type d -links 2

ファイルシステム内の各ディレクトリには、少なくとも 2 つのハード リンク (親ディレクトリ自体と '.' エントリ) があります。サブディレクトリ内の各 '..' エントリは、ディレクトリに新しいハード リンクを追加します。したがって、2 つのハード リンクを持つディレクトリを見つけるだけで済みます。

別の回答で提案されたコマンド

find . -type d -links -3

同じことを行いますが、「ハード リンクが 3 つ未満のディレクトリ」というステートメントを使用します。

答え3

最もソートされたもの:

p="";(find . -type d;echo) | while read d; do [[ $p && $d != $p/* ]] && echo $p; p=$d; done

一行で

a="";(find . -type d;echo )|while read i; do if  [[ (! $i =~ $a) && ("$a" != "") ]]; then echo $a; fi; a=$i;done

投稿者が述べたように:

a="";(find . -type d;echo )|while read i; do [[ $i != $a/* && ! -z "$a" ]] && echo $a; a=$i;done

答え4

読んでくださいman find-mindepthオプションはあなたが望むものです。

関連情報