
我正在嘗試編寫一個bash
腳本,用於搜尋指定目錄樹中的檔案內容是否存在指定的子字串。
單獨使用 的grep
遞歸函數是不夠的,因為我可能需要迭代/
系統的目錄(和所有子目錄),這會導致grep
記憶體不足併中止。因此,我決定使用以下變數來獲取指定目錄樹中所有目錄和子目錄的列表,find
這些變數表示傳遞給腳本的參數。
searchdir=$HOME # passed in a script argument
searchstr="secret" # passed in a script argument
我調用該find
實用程式並將輸出儲存到臨時檔案。
TF=$(mktemp)
find ${searchdir} -type d 1>$TF 2>/dev/null
使用臨時檔案中所有目錄的列表,我繼續使用循環迭代該檔案的行,while-do
以便對每個目錄中的所有檔案執行搜尋。對於grep
,我使用中提供的參數格式這個答案搜尋單一目錄中的所有文件,包括隱藏文件。
cat $TF | while read line || [[ -n $line ]];
do
grepdir="${line}/{*,.*}"
grep -sHn "${searchstr}" ${grepdir}
done
....但是,該程式碼不會產生任何輸出。
我驗證了...
它${TF}
包含所有目錄的正確列表。輸出${grepdir}
變數給出了我期望找到的輸出。
/home/user/{*,.*}
/home/user/.ssh/{*,.*}
/home/user/test/{*,.*}
# ... and so on
如果我grep
使用硬編碼目錄運行命令,特別是該~/test/
目錄,其中包含兩個測試檔案以及應該找到的字串
grep -sHn "${searchstr}" /home/user/test/{*,.*}
...它正確輸出包含子字串“secret”的兩個檔案。
/home/user/test/asdf:7:secret
/home/user/test/test.txt:5:asdfasfdsecretaasdfafd
對我有用的格式是最初在回答討論遞歸使用grep
。如果我這樣做:
cat $TF | while read line || [[ -n $line ]];
do
grep -rn "${line}" -e "${searchstr}"
done
....我得到一些輸出(技術上正確,但有許多重複的條目),但由於正在grep
遞歸地處理目錄並且我有所有目錄的列表,因此我一定會在目錄上多次獲得相同的結果,例如上述根目錄grep
將完全失敗,這是我試圖避免的。
我還應該提到的是,我為了讓它工作而拼命的黑客,例如$(echo "${grepdir}")
作為參數傳遞,也導致沒有結果。
我的想法或理解很可能有誤解bash
。在調用之前不應該bash
擴展變數嗎?我的腳本哪裡出錯了?${grepdir}
grep
答案1
規則#1:當命令或腳本沒有按照您的意願執行時,
查看錯誤訊息。 不要把它們丟進 /dev/null
.
您收到類似錯誤訊息
grep: /home/user/{*,.*}: No such file or directory
grep: /home/user/.ssh/{*,.*}: No such file or directory
grep: /home/user/test/{*,.*}: No such file or directory
但你沒有看到他們。
如果我們看一下重擊(1), 我們看
擴展是在命令列上被分割成單字後執行的。執行了七種擴展:大括號擴展、波形符擴展、參數和變數擴展、命令替換、算術擴展、分詞和路徑名擴展。
展開的順序是:大括號展開;波形符擴展、參數和變數擴展、算術擴展和命令替換(以從左到右的方式完成);分詞;和路徑名擴展。
對於您的情況來說,重要的部分是大括號擴展發生在變數擴展之前。所以,如果你說
grep -sHn "${searchstr}" "${line}"/{*,.*}
然後
- 大括號擴充會將最後一個標記變成
"${line}"/*
and"${line}"/.*
, - 變數擴充會將上面的內容變成
/home/user/*
and/home/user/.*
,然後 - 路徑名擴充會將上面的內容轉換為檔案名稱清單。
但是,當你說
grep -sHn "${searchstr}" ${grepdir}
然後
- 變數擴展將最後一個標記變成
/home/user/{*,.*}
,
然後大括號擴展發生就為時已晚。
grep
尋找名為literal 的檔案/home/user/{*,.*}
。
聚苯乙烯
grep -sHn "${searchstr}" "${line}/{*,.*}"
也不起作用,因為引號會阻止大括號擴展和路徑名擴展的發生。
PPS:你不需要那麼多牙套;
grep -sHn "$searchstr" "$line"/{*,.*}
就可以了。
答案2
grep 在整個系統上遞歸時中止的原因可能不是它無法處理大量數據,而是它在 /proc、/sys 或 /dev 中的一個或另一個偽檔案或裝置檔案上出錯。您可以使用--exclude
命令列上的選項排除有問題的目錄。
它不擴展通配符的原因是因為它們在這一行中被引用:
grepdir="${line}/{*,.*}"
將其更改為這一點可能有助於它們的擴展。
grepdir="${line}/"{*,.*}
實現此目的的另一種方法(代表您編寫較少的腳本)是使用檔案路徑來選擇檔案find
並將檔案路徑傳遞到xargs
進行處理:find / ... -print 0 | xargs -0 ...
然而,無論哪種方式都可能仍然會絆倒原始遞歸 grep 絆倒的任何文件,除非您排除它們。