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-command定義一個fn函數作為some-command主體,就像 Bourne-shell 和大多數類似 Bourne 的 shell 中一樣。
  • () { code } args作為位置參數運行code的匿名函數。args
  • (( $# ))這裡匿名函數的主體是一個算術表達式,它解析為真的if $#(參數數量)非零。
  • ${param-default}(來自 Bourne shell)擴展為$param是否設定了參數或default其他情況。在這裡${1-$REPLY},我們允許直接呼叫我們的函數(如has_subdirs mydir)或作為如下所示的全域限定符函數。
  • $dir/*(ND/Y1)$dir擴展到( )中目錄類型的文件,/包括隱藏文件 ( Dotglob ),如果沒有匹配 ( Nullglob ),也不會出錯。但是對於Y1,我們停在第一場比賽。因此has_subdirs,如果目錄至少包含一個子目錄,則匿名函數(因此)將傳回 true。
  • print -rC1在olumn上列印其參數raw1 C
  • ^+has_subdirs僅限於該has_subdirs函數未 ( ^) 傳回 true 的檔案。

如果必須使用bashshell,只需執行以下操作:

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列印未找到的記錄,然後/在上一筆記錄的開頭。

同樣,要將檔案儲存在 bash 4.4+ 陣列中,您需要透過移動 after-l或add in 來-0在輸出上切換到 NUL 分隔記錄:perl-v 'ORS=\0'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}'
)

在某些系統和檔案系統(例如ext4基於 Linux 的系統上的檔案系統)上,您可能可以依賴這樣一個事實:具有子目錄的目錄的連結計數大於 2(dirdir/.以及每個 的附加連結dir/subdir/..)。

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

find或在任何 shell 中使用:

find . -type d -links -3

這在目錄連結計數始終為 1 的情況下不起作用btrfs(在多個 Linux 發行版中已被替換為預設檔案系統),因此我不建議將其作為通用解決方案。ext4

聯合檔案系統與您的情況相同,例如overlayfs我找到一些合併目錄的連結計數為 1,無論它們是否包含子目錄。

但是,無論在哪裡可用,它都比手動計算子目錄的解決方案具有優勢,即您不需要對目錄進行讀取存取(只需對其父目錄進行搜尋存取)。

答案2

您要尋找的是:

find . -type d -links 2

檔案系統中的每個目錄至少有兩個硬連結 - 父目錄中的目錄本身和“.”入口。子目錄中的每個“..”條目都會在目錄中添加新的硬連結。所以你只需要找到有兩個硬連結的目錄。

另一個答案中建議的命令

find . -type d -links -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

正如op所提到的:

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

答案4

man find。該-mindepth選項就是您想要的。

相關內容