如果父目錄也在列表中,則從列表中刪除路徑
我的標題可能有點奇怪,所以這是我的情況:我有一堆目錄路徑,例如
/a/b /a/b/c /a/b/c/d /a/e/f/g/h /a/e/f/g/h/i/j/k/l /a/e/f/g/m/n/o /a/e/f/g/m/n/p
我想過濾掉列表中已經存在的條目的子路徑的所有行,例如
/a/b /a/e/f/g/h /a/e/f/g/m/n/o /a/e/f/g/m/n/p
目錄路徑是從 獲取的
find
,因此它們應該可靠地按自上而下的順序排列。解析為數組或多行字元串的解決方案都受到歡迎。
我假設路徑名列表可能未排序,並且生成的路徑名列表應該與輸入中的順序相同。我還假設沒有路徑名包含嵌入的換行符。
使用
/bin/sh
:#!/bin/sh set -- while IFS= read -r pathname; do for p do case $pathname in ("$p"/*) continue 2 ;; esac done set -- "$@" "$pathname" done <list printf '%s\n' "$@"
這從文件中讀取路徑名
list
,一次一行。接受的路徑名(最初是一個空列表)針對每個讀取的路徑名進行測試,在內部循環中一次一個。如果接受的路徑名是目前路徑名的目錄路徑前綴,則丟棄目前路徑名(內部循環使用 跳到外部循環的下一次迭代continue 2
)。如果沒有發現接受的路徑名是目前路徑名的目錄路徑前綴,則接受目前路徑名。接受的路徑名列表保存在位置參數中。
bash
shell 顯然可以執行上面的腳本,但是如果你想要專門為那個 shell 編寫的東西,你可以說#!/bin/bash accepted=() while IFS= read -r pathname; do for p in "${accepted[@]}"; do [[ $pathname == "$p"/* ]] && continue 2 done accepted+=("$pathname") done <list printf '%s\n' "${accepted[@]}"
使用
awk
與上述相同的方法:$ awk '{ for (i=1; i<=n; ++i) if (index($0, accepted[i] "/") == 1) next; accepted[++n]=$0 } END { for (i=1; i<=n; ++i) print accepted[i] }' list /a/b /a/e/f/g/h /a/e/f/g/m/n/o /a/e/f/g/m/n/p
awk
程式碼,整理:{ for (i = 1; i <= n; ++i) if (index($0, accepted[i] "/") == 1) next accepted[++n] = $0 } END { for (i = 1; i <= n; ++i) print accepted[i] }
您應該能夠在開始時看到該
awk
程序與 shell 程式碼變體之間的明顯相似之處。這用於
index()
測試接受的路徑名是否是目前路徑名的前綴。您可以if ($0 ~ "^" acceped[i] "/")
改用,但這樣做的缺點是路徑名本身用作正則表達式的一部分。一旦您的路徑名包含諸如等字元,這就會變得很.
重要*
。
如果我沒記錯的話,一個標準化(*)的列表,或者至少是一致呈現的路徑,按照通常的詞典排序,有一個目錄的子目錄立即出現在該目錄之後(遞歸地)。因此,僅查看前一行(未刪除)就足夠了。
(* 通過規範化,我的意思是
/foo/bar
or/foo/bar/
,而不是例如/foo/asdf/../bar
or/foo///bar//
。輸出find
不會有問題,因為如果給定一個非規範化的起始目錄,它確實會給出非規範化的輸出,但輸出至少是一致的。)一條路徑仍然可以是另一個路徑的前綴,但它只是兄弟而不是父路徑,例如
/foo
and/foobar
。為了處理這種情況,我們可以在沒有斜杠的每一行添加一個斜杠。因此(帶有
/foo
並/foobar
添加到測試中,並且沒有嘗試打高爾夫球的程式碼):$ sort paths.txt | awk '! /\/$/ { $0 = $0 "/" } last && last == substr($0, 1, length(last)) { next; } { last = $0; sub(/\/$/, "", $0); print }' /a/b /a/e/f/g/h /a/e/f/g/m/n/o /a/e/f/g/m/n/p /foo /foobar
如果需要,第一行將斜杠添加到目前行
$0
;第二個將該行與最後儲存的行(在 中last
)進行比較(如果有的話),並刪除匹配的行;第三個儲存並列印任何未刪除的行,並刪除尾部斜杠。(刪除sub(...)
以保留它們。)