Подтвердить что ты не робот

"bash: Плохая замена" при использовании кода в .sh файле

Почему

for i in *.mp4; do ffmpeg -i "$i" "${i/.mp4/.mp3}"; done

работать, если я использую его в консоли, но даю мне ошибку "Bad substitution", если я использую тот же код в файле .sh?

for i in *.mp4
do
    ffmpeg -i "$i" "${i/.mp4/.mp3}"
done
4b9b3361

Ответ 1

Значение #!/bin/sh

Это потому, что вы используете #!/bin/sh в своем script, в качестве исправления вы должны изменить его на #!/bin/bash.

#!/bin/bash
for i in *.mp4
do
    ffmpeg -i "$i" "${i/.mp4/.mp3}"
done

Люди используют #!/bin/sh, когда используют только ограниченный набор функций (определенный стандартом POSIX) для максимальной переносимости. #!/bin/bash отлично подходит для пользовательских скриптов. /bin/sh обычно символически связывается с минимальной оболочкой, совместимой с POSIX, или с стандартной оболочкой (например, bash). В более позднем случае bash запускается в режиме совместимости, что объясняется в manpage:

Если bash вызывается с именем sh, он пытается как можно ближе имитировать поведение при запуске исторических версий sh, в то же время соответствующее стандарту POSIX.

Предлагаемые изменения в script:

  • Считается хорошей практикой использовать кавычки вокруг переменных ("$i" вместо $i). Котируемые переменные будут предотвращать проблемы, если сохраненное имя файла содержит пробелы.
  • Мне нравится, что вы используете расширенное расширение . Я предлагаю использовать "${i%.mp4}.mp3" (вместо "${i/.mp4/.mp3}"), так как ${parameter%word} заменяет только в конце (например, файл с именем foo.mp4.backup).

Ответ 2

Конструкция ${var/x/y/} не POSIX. В вашем случае, когда вы просто удаляете строку в конце переменной и применяете другую строку, переносимое решение POSIX должно использовать

#!/bin/sh
for i in *.mp4; do 
    ffmpeg -i "$i" "${i%.mp4}.mp3"
done

или даже короче, ffmpeg -i "$i" "${i%4}3".

Определяющим допированием для этих конструкций является глава Расширение параметров для оболочки POSIX.