Bash 變數在傳遞到 ffmpeg 時被截斷
我有一張包含 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
所以文件路徑中的前導
./0
s 沒有傳達給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
截斷
th
inthree.flac
。**編輯:**我不小心發現管道似乎可以解決這個問題,因為我不想被提示覆蓋現有
yes |
文件。ffmpeg``.wav
**編輯:**感謝@roaima 的解釋!事實證明,
ffmpeg
並read
從管道共享標準輸入句柄,因此可以在有機會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
看