2015-10-13 18 views
3

我想編寫一個bash腳本(其中包括)找到Makefile,其中Makefile與python文件位於同一目錄中(文件看起來像* .py)。如何打印包含兩個匹配兩種不同模式的文件的目錄?

我可以通過執行兩個單獨的查找和比較輸出在幾個步驟中不恰當地完成它,但我認爲可能有辦法執行一行查找命令?它似乎應該有一種方式?

所以

path1/Makefile 
path1/Some.py 
path2/Makefile 
path3/Makefile 
path3/path4/Makefile 
path3/path4/Another.py 

PATH1將被打印。

path2不會。

path3不會被打印。 b/c python文件需要處於同一級別。

path3/path4將被打印。

所以一般的問題是,我可以使用find來查找包含至少兩個文件的目錄,一個匹配一個模式,另一個匹配另一個模式,但是這兩個模式都必須由單獨的文件滿足?

謝謝。

btw,我使用的是 find(GNU findutils)4.4.2。

但是,我只是感興趣的人提出的答案。我已經完成了不雅的解決方案,但很高興看到。這總是有益的和教育的。

回答

4

我想不出一種方式獨自找條件要做到這一點,但當然,你可以隨時使用-exec構造是這樣的:

find . -name '*.py' -exec bash -c 'test -f $(dirname "$1")/Makefile' -- {} \; -print 

這將打印的其中有一個Makefile文件在同一目錄下的* .py文件列表。如果您在最後一個正斜槓後刪除所有內容,則只需找到包含這些文件的目錄,例如通過管道sed 's:/[^/]*$::'

該方案具有隻運行單一find,在產卵每一個殼,發現每一個.py文件的成本優勢。請注意,test是大多數炮彈中的內建炮彈。


你可以交替做在bash獨自繞過find乾脆:

shopt -s globstar 
for file in **/./*.py; do 
    test "${file%/*}/Makefile" && echo "${file%/*}" 
done | uniq 

的「globstar」選項允許**/*.py通過子目錄進行搜索,find做,以及與此解決方案,我們可以使用參數擴展代替運行dirname的子shell。正如您所要求的,輸出是包含符合條件的文件的目錄列表。在單個目錄中存在多個*.py匹配的情況下,通過uniq過濾輸出。每伊坦的評論

更新:

注意的是,爲了適應含*的.py和Makefile中當前(最上面的)目錄的可能性,水珠是**/./*.py而不是**/*.py。結果導致每個匹配路徑以點結束。雖然這仍然會找到所有目標目錄(/foo/bar/./foo/bar相同),但如果它困擾您,則可以在uniq之後通過添加| sed 's:/\.::'來去除尾部點路徑段。添加這樣的過濾器將使輸出顯示與基於dirname的解決方案相同的方式。

+0

如果您的'find'具有'-exec \ +'可能會有用,因爲您可以在內聯腳本中循環參數並單獨測試每個參數,然後直接從那裏進行打印(並跳過'-print')。 –

+0

優秀的建議。在FreeBSD中可以正常工作,但是我現在將這個答案保留下來,因爲我不知道'-exec ... +'的背景。 – ghoti

+0

在我的回答中添加了未經測試的建議修改版本。 –

-1

find有一個-o標誌來組合多個模式。像這樣:

find \([pattern for path1] -o [pattern for path2] \) 
+2

這將找到匹配*或*謂詞的文件,但不能找到匹配*既*的文件的文件。 –

+0

@EtanReisner啊我明白了。對不起,我完全誤解了你的問題。 – matz

1

假設你只想打印出直接的普通父目錄,而不是匹配的文件名,那麼我認爲這將起作用。 (雖然我覺得有必要有更好的方法來做到這一點)。

注意:需要GNU find-printf

find "$topdir" -name '*.py' -printf '%h\0' | xargs -0 -I {} find {} -mindepth 1 -maxdepth 1 -name Makefile -printf '%h\n' | sort -u 

對於非GNU find選項(從ghoti's handiwork(未經測試改性):

find . -name '*.py' -exec sh -c 'for file; do d=$(dirname "$file"); test -f "$d"/Makefile && printf "%s\n" "$d"; done' -- {} \+ 

-exec ;一次運行命令爲每個匹配的文件-exec +組裝與儘可能多的文件作爲將單個命令行適合單一命令運行很多對於大型匹配文件列表shell的實例較少。

+0

我的'find'沒有'-printf'選項。你是否使用與海報相同的操作系統? – ghoti

+0

@ghoti他沒有指定,所以我不知道。我沒有意識到'-printf'雖然是一個GNU選項(儘管我可能應該有)。 –

+2

試過最後一次編輯,看起來沒有工作,似乎一遍又一遍地列出同一個目錄。 – Bitdiot

2

使用bash試試這個:

shopt -s globstar nullglob 
for i in **/Makefile; do i="${i%/*}"; x=("$i"/*.py); [[ -n ${x[0]} ]] && echo "$i"; done 

輸出:

shopt -s nullglob 

while IFS= read -rd '' dir; do 
    ary=("$dir"/*.py) 
    [[ -f "$dir"/Makefile && ${#ary[@]} -gt 0 ]] && echo "$dir" 
done < <(find . -type d -print0) 

find

 
path1 
path3/path4 
1

我會在while read循環使用進程替換表明這find命令將查找當前目錄中的所有目錄,並在while循環中查找存在*.py文件和Makefile的每個目錄。

+0

你爲什麼會這麼建議?這個解決方案如何改進ghoti和Cyrus的bash解決方案? – Graham

+0

因爲1.它避免爲子目錄中的每個python文件分派一個子shell。 2.由於在BASH 4中引入了globstar,因此可以使用舊的bash版本(<4) – anubhava

相關問題