Bash

操作從 find 命令管道傳輸的文件名

  • January 6, 2013

我對 Bash 比較陌生,並且正在嘗試做一些表面上看起來很簡單的事情 - 在目錄層次結構上執行 find 以獲取所有 *.wma 文件,將該輸出通過管道傳輸到我將它們轉換為 mp3 的命令和將轉換後的文件另存為 .mp3。我的想法是該命令應該如下所示(我已經放棄了音頻轉換命令,而是使用 echo 進行說明):

$ find ./ -name '*.wma' -type f -print0 | xargs -0 -I f echo ${f%.*}.mp3

據我了解, -print0 參數將讓我處理有空格的文件名(其中許多文件名是音樂文件)。然後我期望(作為xargs的結果)find的每個文件路徑都在f中擷取,並且使用字元串末尾的子字元串匹配/刪除,我應該用mp3回顯原始文件路徑副檔名而不是 wma。但是,我看到的不是這個結果,而是以下內容:

*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
...

所以我的問題(除了具體的“我在這裡做錯了什麼”之外)是這樣的 - 在字元串操作操作中,作為管道操作結果的值是否需要與作為變數分配結果的值區別對待?

正如其他答案已經確定的那樣,在執行命令${f%.*}之前由 shell 擴展。xargs您需要為每個文件名發生一次此擴展,並將 shell 變數f設置為文件名(傳遞-I f不會這樣做:xargs沒有 shell 變數的概念,它會f在命令中查找字元串,所以如果你例如xargs -I e echo …,它會執行類似./somedir/somefile.wmacho .mp3) 的命令。

繼續使用這種方法,告訴xargs呼叫可以執行擴展的 shell。更好的是,tell findxargs是一個很大程度上過時的工具,並且很難正確使用,因為現代版本的 find 具有一個可以完成相同工作(以及更多工作)且管道困難更少的構造。而不是find … -print0 | xargs -0 command …,執行find … -exec command … {} +

find . -name '*.wma' -type f -exec sh -c 'for f; do echo "${f%.*}.mp3"; done' _ {} +

論據_$0外殼中;for f; do …文件名作為循環的位置參數傳遞。此命令的一個更簡單的版本為每個文件執行一個單獨的 shell,它是等效的,但速度稍慢:

find . -name '*.wma' -type f -exec sh -c 'echo "${0%.*}.mp3"' {} \;

您實際上不需要在find此處使用,假設您執行的是相當新的 shell(ksh93、bash ≥4.0 或 zsh)。在 bash 中,放入shopt -s globstar您的.bashrc以啟動**/glob 模式以在子目錄中遞歸(在 ksh 中,即set -o globstar)。然後你可以執行

for f in **/*.wma; do
 echo "${f%.*}.mp3"
done

(如果您有名為 的目錄,請在循環開頭*.wma添加。)[ -f "$f" ] || continue

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