Shell-Script

遞歸遍歷所有子目錄,如果存在具有特定副檔名的文件,則在該文件夾中執行一次命令

  • January 23, 2018

我需要遞歸遍歷文件夾的所有子目錄。在子目錄中,如果有一個副檔名為“.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

嘗試這樣的事情(使用 GNUfindutils和 GNU sort):

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 腳本。

引用自:https://unix.stackexchange.com/questions/418988