Find

您可以在“查找”期間修改文件名嗎

  • June 16, 2022

find用於{}表示“這個文件”(ish)。您可以將一系列文件輸入myprog,因此:

find ./tests/ -name *.in -exec myprog -i {} \;

有沒有辦法修改名稱{}?在我的例子中,我-i用來定義一個輸入文件和-o輸出,我希望輸出轉到一個稍微修改過的文件名,這樣“a.in”就會產生“a.out”。理想情況下,我想要一些效果:

find ./tests/ -name *.in -exec myprog -i {} -o {}.out \;

此外,輸出目錄可能是不同的路徑。在這種情況下,輸出不會去,/tests/但也許/tests_20220615/

我查看了許多帶有 範例的頁面find,但沒有出現這樣的情況,所以也許“不”?

我知道有一些方法可以使用 bash 或 zsh 中的循環來做到這一點,但是可能的陷阱列表很棒(“nullglob”?!),如果find可以做到這一點,對於這個菜鳥來說似乎更安全。

find不允許您修改文件的路徑,某些find實現不允許您與其他內容連接{},有些甚至不支持{}多次傳遞,但您始終可以執行一些命令,例如可以進行轉換的 shell:

find ./tests/ -name '*.in' -type f -exec sh -c '
 ret=0
 for file do
   myprog -i "$file" -o "${file%.*}.out" || ret="$?"
 done
 exit "$ret"' sh {} +

上面,myprog我們不是直接執行,而是執行sh並傳遞一些內聯程式碼以及找到的文件的路徑(盡可能多地傳遞{} +而不是傳遞)。{} ';'

sh依次循環這些文件,並myprog在對它們應用轉換後呼叫,例如${file%.*}刪除副檔名。

注意周圍的引號*.in。如果沒有它們,您在其中執行該find命令的 shell 會嘗試將其擴展為目前目錄中名稱以結尾的文件列表,.in而不是將該模式逐字傳遞給find.

上面,我們告訴如果任何呼叫失敗sh,則以失敗退出狀態退出。myprog該失敗將反映在 的退出狀態中find,因此您可以在需要時採取措施,或者在errexit啟用該選項時退出腳本。但是,在第一次失敗時中止是不可能的myprog

如果使用zshshell,您也可以在內部進行查找:

set -o errexit
for file (./tests/**/*.in(ND.)) myprog -i $file -o $file:r.out

將在第一次失敗時退出,並且還會按詞法順序處理列表(您始終可以添加oNglob 限定符以禁用該排序)。

另一種方法是find列印文件,將其通過管道傳輸到執行轉換的某個命令,然後通過管道傳輸到xargs. 例如:

find ./tests/ -name '*.in' -type f -print0 |
 gawk -v RS='\0' -v ORS='\0' -v OFS='\0' '
   {
     filein = fileout = $0; sub(/\.in$/, ".out", fileout)
     print "-i", filein, "-o", fileout
   }' | xargs -r0 -n4 myprog

如果任何呼叫失敗,將再次xargs返回非零退出狀態。myprogGNU可以使用它的選項xargs並行執行多個呼叫。-P

或者你可以perl對它進行後期處理並讓它執行:

find ./tests/ -name '*.in' -type f -print0 |
 perl -l -0ne '
   system("myprog", "-i", $_, "-o", s/\.in\Z/.out/r) == 0 or
     $ret = 1;
   END {exit $ret}'

請注意,後處理其輸出的方法find將掩蓋其失敗退出狀態(如果有)(例如當它無法進入某些目錄時),除非您設置pipefailshell(如果支持)。

使用管道也會暗示myprog標準輸入將是什麼(例如,如果它需要提示使用者)。GNUxargs打開標準輸入/dev/null,其他一些perl方法將保持原樣,這意味著它將是來自find/的管道gawk

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