36

端末で正常に動作するコード行があります。

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

次に、まったく同じコード行をスクリプトに入れますmyscript.sh

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

ただし、実行時にエラーが発生するようになりました。

$ sh myscript.sh
myscript.sh: 2: myscript.sh: Bad substitution

他の質問に基づいて、シバン#!/bin/bashに変更しようとしましたが、まったく同じエラーが発生します。このスクリプトを実行できないのはなぜですか?

4

2 に答える 2

62

TL; DR:Bash固有の機能を使用しているため、スクリプトはsh:ではなくBashで実行する必要があります。

$ sh myscript.sh
myscript.sh: 2: myscript.sh: Bad substitution

$ bash myscript.sh
ffmpeg -i bar.mp4 bar.mp3
ffmpeg -i foo.mp4 foo.mp3

shとBashの違いを参照してください。使用しているshを見つけるには:readlink -f $(which sh)

bash固有のスクリプトが常に正しく実行されるようにするための最良の方法

ベストプラクティスは両方にあります:

  1. (またはスクリプトが依存する他のシェル)に#!/bin/sh置き換えます。#!/bin/bash
  2. このスクリプト(および他のすべてのスクリプト)を、./myscript.shまたは/path/to/myscript.shで、先頭にshまたはを付けずに実行しますbash

次に例を示します。

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

$ chmod +x myscript.sh   # Ensure script is executable

$ ./myscript.sh
ffmpeg -i bar.mp4 bar.mp3
ffmpeg -i foo.mp4 foo.mp3

(関連:なぜ./スクリプトの前にあるのですか?

の意味#!/bin/sh

シバンは、システムがスクリプトを実行するために使用するシェルを提案します。これにより、#!/usr/bin/pythonまたはを指定できる#!/bin/bashため、どのスクリプトがどの言語で書かれているかを覚えておく必要がありません。

#!/bin/sh移植性を最大化するために、限られた機能セット(POSIX標準で定義されている)のみを使用する場合に使用します。#!/bin/bash便利なbash拡張機能を利用するユーザースクリプトにはまったく問題ありません。

/bin/sh通常、最小限のPOSIX準拠シェルまたは標準シェル(bashなど)のいずれかにシンボリックリンクされます。後者の場合でも、マニュアルページで説明されているように互換モードで実行される#!/bin/shため、失敗する可能性があります。bash

bashがshという名前で呼び出された場合、POSIX標準にも準拠しながら、shの履歴バージョンの起動動作を可能な限り模倣しようとします。

の意味sh myscript.sh

shebangは、、を実行する場合、または拡張機能を削除し、スクリプトをのディレクトリに配置して、を実行する場合にのみ使用./myscript.sh/path/to/myscript.sh$PATHますmyscript

インタプリタを明示的に指定すると、そのインタプリタが使用されます。シェバンが何を言おうと、sh myscript.shそれを強制的に実行します。shこれが、シェバンを変更するだけでは不十分な理由です。

スクリプトは常に優先インタープリターを使用して実行する必要があるため、スクリプトを実行するときは常に優先./myscript.shまたは同様の方法で実行してください。

スクリプトに対するその他の提案された変更:

  • "$i"(の代わりに)変数を引用することをお勧めします$i。保存されたファイル名に空白文字が含まれている場合、引用符で囲まれた変数は問題を防ぎます。
  • 高度なパラメータ拡張を使用するのが好きです。最後に(たとえば、という名前のファイル)を置き換えるだけなので、 "${i%.mp4}.mp3"(の代わりに"${i/.mp4/.mp3}")を使用することをお勧めします。${parameter%word}foo.mp4.backup
于 2013-03-03T12:28:28.347 に答える
10

${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 シェルのパラメータ拡張に関する章です。

于 2013-03-03T17:18:57.457 に答える