您可以在“查找”期間修改文件名嗎
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
。如果使用
zsh
shell,您也可以在內部進行查找:set -o errexit for file (./tests/**/*.in(ND.)) myprog -i $file -o $file:r.out
將在第一次失敗時退出,並且還會按詞法順序處理列表(您始終可以添加
oN
glob 限定符以禁用該排序)。另一種方法是
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
返回非零退出狀態。myprog
GNU可以使用它的選項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
將掩蓋其失敗退出狀態(如果有)(例如當它無法進入某些目錄時),除非您設置pipefail
shell(如果支持)。使用管道也會暗示
myprog
標準輸入將是什麼(例如,如果它需要提示使用者)。GNUxargs
打開標準輸入/dev/null
,其他一些perl
方法將保持原樣,這意味著它將是來自find
/的管道gawk
。