Bash

Bash 變數在傳遞到 ffmpeg 時被截斷

  • September 22, 2022

我有一張包含 11 個.flac音頻文件的專輯:

> find . -name "*.flac"
./02. Like Napalm.flac
./10. Land of the Lost.flac
./09. Stranger.flac
./08. Soul Bleach.flac
./07. If a God Can Bleed.flac
./03. Glitch.flac
./04. The Greatest Fear.flac
./05. Darker Still.flac
./01. Ground Zero.flac
./11. From the Heart of the Darkness.flac
./06. Imperial Heretic.flac

我將這些轉換為.wav具有特定採樣率和位深度的文件。我ffmpeg在 Bash shell 中使用過。如果我手動呼叫它,像這樣的命令對 11 個文件中的每一個都非常有效:

ffmpeg -i "./06. Imperial Heretic.flac" -sample_fmt s16 -ar 44100 "./06. Imperial Heretic.wav"

但是,當我編寫一個while循環來為每個文件執行此命令時:

find . -name "*.flac" | sed "s/\.flac//" | while read f; do ffmpeg -i "$f.flac" -sample_fmt s16 -ar 44100 "$f.wav"; done

這有效,但僅適用於 8/11 的文件,並ffmpeg為三個失敗提供以下錯誤消息:

9. Stranger.flac: No such file or directory
5. Darker Still.flac: No such file or directory
6. Imperial Heretic.flac: No such file or directory

所以文件路徑中的前導./0s 沒有傳達給ffmpeg,而僅用於這些文件;找到其他八個沒有問題。其他八個中的一些已./被截斷,但這會導致有效路徑,因此ffmpeg能夠繼續。

如果我使用相同的循環結構,但使用echo "$f"而不是呼叫ffmpeg,則輸出沒有問題:

> find . -name "*.flac" | sed "s/\.flac//" | while read f; do echo "$f"; done
./02. Like Napalm
./10. Land of the Lost
./09. Stranger
./08. Soul Bleach
./07. If a God Can Bleed
./03. Glitch
./04. The Greatest Fear
./05. Darker Still
./01. Ground Zero
./11. From the Heart of the Darkness
./06. Imperial Heretic

所以我確信循環的結構很好,"$f"在每次迭代中都可以準確地擴展我的期望。不知何故,當傳遞"$f.flac"到時ffmpeg,部分字元串被截斷,但僅限於某些文件。

我只是想了解為什麼我的第一次嘗試表現出這種行為。我不是在尋找循環或轉換文件的替代方法(我的第二次嘗試,使用不同類型的循環,對所有 11 個文件都成功)。


**編輯:**這與分詞或去除前導零無關。即使我one.flac通過重命名文件eleven.flac,也會出現相同的錯誤消息,例如:

ree.flac: No such file or directory

截斷thin three.flac

**編輯:**我不小心發現管道似乎可以解決這個問題,因為我不想被提示覆蓋現有yes |文件。ffmpeg``.wav

**編輯:**感謝@roaima 的解釋!事實證明,ffmpegread從管道共享標準輸入句柄,因此可以在有機會ffmpeg之前從每行的開頭消耗字元,從而破壞一些文件路徑。read這解釋了為什麼yes | ffmpeg ...有效,因為它提供ffmpeg了不同的標準輸入。原始循環與ffmpeg -nostdin .... 請參閱http://mywiki.wooledge.org/BashFAQ/089

除非您有文件目錄樹(未提及,也未在範例數據集中顯示),否則您可以使用簡單的循環來處理文件

for f in *.flac
do
   w="${f%.flac}.wav"
   ffmpeg -i "$f" -sample_fmt s16 -ar 44100 "$w"
done

如果您確實有文件層次結構,則可以將其合併到find搜尋中

find -type f -name '*.flac" -exec sh -c 'for f in "$@"; do w="${f%.flac}.wav"; ffmpeg -i "$f" -sample_fmt s16 -ar 44100 "$w"; done' _ {} +

為了提高效率,您可能希望跳過任何flac已經具有相應wav. w=賦值後,添加[ -s "$w" ] && continue. 如果你真的不想這樣,那麼你可以進一步優化命令,

find -type f -name '*.flac" -exec sh -c 'ffmpeg -i "$1" -sample_fmt s16 -ar 44100 "${1%.flac}.wav";' _ {} \;

ffmpeg對於執行前測試,加上前綴echo以查看在沒有實際執行的情況下會執行什麼。(不過,不會顯示引號,因此請記住這一點。)


事實證明,問題實際上是關於ffmpeg咀嚼它應該處理的文件名。這是因為從stdinffmpeg讀取命令,並且文件列表已呈現為循環的管道(也使用stdin)。while read …

解決方案:ffmpeg -nostdin …ffmpeg … </dev/null

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