遞歸遍歷所有子目錄,如果存在具有特定副檔名的文件,則在該文件夾中執行一次命令
我需要遞歸遍歷文件夾的所有子目錄。在子目錄中,如果有一個副檔名為“.xyz”的文件,那麼我需要在該文件夾中執行一次特定命令。
這是我到目前為止所擁有的
recursive() { for d in *; do if [ -d "$d" ]; then (cd -- "$d" && recursive) fi dir=`pwd` pattern="*.xyz" file_count=$(find $dir -name $pattern | wc -l) if [[ $file_count -gt 0 ]]; then echo "Match found. Going to execute a command" #execute command fi done } (cd /target; recursive)
但問題是當有匹配時,每個文件夾會多次顯示“找到匹配..”消息。在解決此問題時是否有更簡單的方法來執行此操作?
find
有一個內置標誌來列印字元串,這在這裡非常有用:
find -iname "*.xyz" -printf "%h\n"
列印包含與您的模式匹配的文件的所有目錄的名稱(%h
只是find
擴展到文件目錄的魔術語法,\n
當然是換行符)。因此,這可以滿足您的要求:
COMMAND='echo' find `pwd` -iname "*.pdf" -printf "%h\n" | sort -u | while read i; do cd "$i" && pwd && $COMMAND done
這裡發生了一些事情。要只執行一次命令,我們只需
sort
使用-u
標誌將其通過管道傳遞,這將刪除所有重複的條目。然後我們用 循環遍歷所有內容while
。另請注意,我使用find
pwd``了 ,這是製作find
輸出絕對路徑而不是相對路徑的一個很好的技巧,它允許我們使用cd
而不必擔心任何相對路徑。編輯:執行此腳本時請注意目錄名稱,因為目錄名稱包含換行符 (
\n
) 甚至\
可能會破壞腳本(可能還有其他不常見的字元,但我還沒有測試過)。解決這個問題很困難,我不知道該怎麼做,所以我只能建議不要使用這樣的目錄。
你在重新發明
find
。嘗試這樣的事情(使用 GNU
findutils
和 GNUsort
):find /target -iname '*.xyz' -printf '%h\000' | sort -z -u | xargs -0 -r -I {} sh -c "cd {} ; yourcommandhere"
-printf
列印%h
找到“*.xyz”文件的目錄名稱 (),並以 NUL 字節 ( )\000
作為分隔符。sort
用於消除重複,然後xargs
用於cd
進入每個目錄並執行yourcommandhere
.您還可以編寫一個腳本以使用 xargs 執行。例如
find /target -iname '*.xyz' -printf '%h\000' | sort -z -u | xargs -0 -r /path/to/myscript.sh
簡單的 myscript.sh 範例:
#!/bin/sh for d in "$@" ; do cd "$d" echo "Match found in $d. Going to execute command" # execute command done
如果有許多匹配的目錄,第二個版本將明顯更快 - 它只需要分叉一次 shell(然後迭代每個參數),而不是每個目錄分叉一次 shell。
順便說一句,這裡既不需要也不
printf
需要……但它們確實使閱讀和理解正在發生的事情變得更加容易。同樣重要的是,通過儘早消除重複項(使用 printf 和 sort),它的執行速度比僅使用 bash 快得多,並且消除了在任何給定目錄中多次執行命令的風險(相當小)。sort``xargs
這是做同樣事情的另一種方法,沒有 sort 或 xargs:
find /target -iname '*.xyz' -exec bash -c \ 'typeset -A seen for f in "$@"; do d="$(dirname "$f")"; if [[ ! -v $seen[$d] ]]; then echo "Match found in $d. Going to execute command" # Execute command seen["$d"]=1 fi done' {} +
這使用 bash ( ) 中的關聯數組
$seen[]
來跟踪哪些目錄已被查看和處理。請注意,如果有數千個匹配*.xml
文件(足以超過最大命令行長度,因此 bash 腳本會被多次分叉),那麼您的命令可能會在任何給定目錄中執行多次。find 的
-exec
選項執行的腳本可以是一個獨立的腳本,就像上面的 xargs 版本一樣。順便說一句,這裡的任何變體都可以輕鬆地執行 awk 或 perl 或任何腳本,而不是 sh 或 bash 腳本。