[英]grep a keyword in all .h and .c files under current directory but exclude two directories
我想在當前目錄下的所有.h
和.c
文件中grep一個關鍵字./
但在輸出中排除兩個目錄./stubdom
和./dist
。
我搜查,試過並測試了幾個命令; 最后我認為一個shell工作:
find . -type d \( -path "./stubdom/*" -o -path "./dist/*" \) -prune -o -regex '.*\.\(h\|c\)$' -print | xargs grep map_foreign_range
此shell正在查找所有.h和.c文件並排除./stubdom/和./dist路徑:
find . -type d \( -path "./stubdom/*" -o -path "./dist/*" \) -prune -regex '.*\.\(h\|c\)$' -print | xargs grep map_foreign_range
但是,上面的命令不起作用!
(我在正則表達式之前刪除-o以獲得AND操作!)
但是,我不太明白為什么會這樣。 我有幾個問題:
\\( -path "./stubdom/*" -o -path "./dist/*" \\)
這是一個find的動作,但它是如何工作的? 為什么它不是\\( -path "./stubdom/*" -o -path "./dist/*" -o \\)
(我在末尾添加了另一個-o)。
如果我把-regex
的前-type
,它會打印出.o文件,這意味着你-regex
如果它之前把不工作-type
。 我的問題是:find命令的選項有從左到右的執行順序?
有沒有更簡潔的方法來實現我的目標:在當前目錄下的所有.h
和.c
文件中grep一個關鍵字,但排除兩個目錄?
-o
運算符是'或'運算符。 第二條路徑之后的-o
需要在它之后進行另一次測試。 帶括號的表達式也受條件-type d
和-prune
約束。 總的來說,該術語表示'如果當前名稱是目錄,並且路徑與路徑表達式匹配,那么搜索將被修剪',這意味着搜索不會繼續
find
的一般操作是它搜索目錄列表,並對搜索表達式求值為true的目錄下找到的每個名稱執行某些操作。
您當前的命令是:
find . -type d \\( -path "./stubdom/*" -o -path "./dist/*" \\) -prune -o -regex '.*\\.\\(h\\|c\\)$' -print
我要放棄這個find .
部分,將其視為其余答案的假設。 我還將使用名稱A
和B
代替stubdom
和dist
來縮短它,以便一切都可見。
我們當然可以通過用-name
替換-regex
來簡化它:
-type d \\( -path "./A/*" -o -path "./B/*" \\) -prune -o -name '*.[ch]' -print
請注意,條件之間的默認連接是'和'。 使用C或shell表示法&&
和||
,我們可以看到表達式的形式如下:
(-type d && ( ... ) && -prune) || (-name '*.[ch]' && -print)
當您在-type
之前移動-regex
(現在為-name
)時,將表達式重寫為:
(-name '*.[ch]' && -type d && ( ... ) && -prune) || (-print)
因此,出現目標文件名的原因是無條件地應用了打印。
我的實驗表明, -path
上的/*
是適得其反的。
要演示,創建一個垃圾目錄, cd
進入它,然后運行:
mkdir a b c d
for d in a b c d
do
for file in abc def pqr zyz
do
for ext in c h
do cp /dev/null $d/$file.$ext
done
done
done
現在運行:
find . -name '*.[ch]' | wc -l
這給出了答案32。
現在運行:
find . -type d \( -path "./a/*" -o -path "./b/*" \) -prune -o -name '*.[ch]' -print | wc -l
這也給出了32。
刪除-path
操作數的/*
部分,然后得到16.刪除wc
顯示16個名稱是c
和d
下的文件,這些是需要的文件。
find . -type d \( -path "./a" -o -path "./b" \) -prune -o -name '*.[ch]' -print
因此,應用於您的場景,您應該能夠使用:
find . -type d \( -path "./stubdom" -o -path "./dist" \) -prune -o -name '*.[ch]' -print
但是,您最好完全避免使用xargs
:
find . -type d \( -path "./a" -o -path "./b" \) -prune -o -name '*.[ch]' \
-exec grep map_foreign_range {} +
如果任何文件名或目錄名包含空格(或制表符或換行符),則可以避免出現問題。 您也可以解決,隨着-print0
足月find
和-0
選項xargs
,如果您對這些命令的版本支持的符號(GNU做,也是如此的Mac OS X,因此可能是其他BSD變體也是如此)。
( 測試在Mac OS X 10.9.1完成與系統(BSD) find
,不與GNU find
。)
在這里,我會盡力回答你:
-o
。 如果任何-path "./stubdom/*"
和-path "./dist/*"
,則行\\( -path "./stubdom/*" -o -path "./dist/*" \\)
將被評估為True
-path "./dist/*"
將匹配。 -o
是一個邏輯OR
,它是一個二元運算符,所以它需要兩個參數。 如果沒有別的,你不能在最后附加它。 -o
。 如果你沒有在-type d
和-regex ...
之間放置一個OR
-regex ...
find將只查找與regexp匹配的目錄。 而不是任何目錄或匹配正則表達式的東西。 順便說一句,是的,因為找到選項的順序絕對相關。 總結你的線如何工作,它等同於這個偽代碼:
if(isdir(file) and file != "./stubdom/*" and file != "./dist/*")
print file;
else if (regex(file, '.*\.\(h\|c\)$' and file != "./stubdom/*" and file != "./dist/*")
print file;
編輯:
閱讀我記得關於grep的--exclude-dir
選項的評論。 試試吧。 它可能是更簡潔的解決方案。
\\( -path "./stubdom/*" -o -path "./dist/*" \\)
是-prune
的過濾器,因此應排除這些目錄。 它不能是\\( -path "./stubdom/*" -o -path "./dist/*" -o \\)
,這可能是一個錯誤。 find
實際搜索,所以匹配約束被丟棄。 grep
還有排除文件的選項(例如--exclude-dir
等)。 您還可以嘗試以下命令:
find | awk '(! (/stubdom\// || /dist\//)) && /\.(c|h)$/ {
r=system ("grep -q map_foreign_range "$0)
if(!r) print
}'
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.