2013-03-02 4 views
27

Warum"bash: Bad Substitution" bei der Verwendung von Code in .Sh Datei

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

Arbeit, wenn ich es in der Konsole, aber geben Sie mir eine "Bad Substitution" Fehler, wenn ich den gleichen Code verwenden in einer SH-Datei?

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

Verwenden Sie #!/Bin/sh oder #!/Bin/bash aus dem Skript? –

+1

In der Konsole wurden die Befehle von 'bash' Shell ausgeführt. Wenn Sie '#!/Bin/sh' in Ihrem Skript verwenden, versucht die' sh' Shell auszuführen und daher der Fehler. – jitendra

+0

Ich schrieb #!/Bin/sh in der ersten Zeile ... – WhatIsName

Antwort

51

Die Bedeutung von #!/bin/sh

Dies liegt daran, Sie #!/bin/sh in Ihrem Skript verwenden, als fix Sie es #!/bin/bash ändern sollte.

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

Menschen verwenden #!/bin/sh, wenn die nur eine begrenzte Anzahl an Funktionen verwenden (durch den Standard POSIX definiert) für maximale Portabilität. #!/bin/bash ist perfekt für Benutzer-Skripts. /bin/sh ist normalerweise mit einer minimalen POSIX-kompatiblen Shell oder mit einer Standard-Shell (z. B. bash) verknüpft. Im letzteren Fall wird im Kompatibilitätsmodus bash ausgeführt, die in den manpage erklärt:

Wenn bash mit dem Namen sh aufgerufen wird, versucht er, das Startverhalten der historischen Versionen von sh zu imitieren so eng wie möglich, auch nach dem POSIX-Standard.

Empfohlene Änderungen an Ihrem Skript:

  • Es ist gute Praxis betrachtet Anführungszeichen um Variablen zu verwenden ("$i" statt $i). Angegebene Variablen verhindern Probleme, wenn der gespeicherte Dateiname Leerzeichen enthält.
  • Ich mag, dass Sie erweiterte parameter expansion verwenden. Ich schlage vor, "${i%.mp4}.mp3" (anstelle von "${i/.mp4/.mp3}") zu verwenden, da ${parameter%word} nur am Ende ersetzt (zum Beispiel eine Datei mit dem Namen foo.mp4.backup).
+0

+1 - Es hängt auch davon ab, wie es ausgeführt wird, I.E. Du kannst nicht als '#!/Bin/bash' deklarieren, dann mit' sh' aufrufen oder als '#!/Bin/sh' deklarieren und mit' bash' aufrufen, zumindest auf Debian 7 wirst du auf beiden einen Fehler sehen . Die Deklaration muss dem Aufruf entsprechen. – webLacky3rdClass

+0

@CharlesDuffy Das war ein Tippfehler/Verwirrung, ich meinte wahrscheinlich Zitate. –

+0

Ich denke mit '#!/Usr/bin/env bash' wäre ein besserer Weg. – Hozefa

9

Das ${var/x/y/} Konstrukt ist nicht POSIX. In Ihrem Fall, wo man nur einen String am Ende einer Variablen und Tack auf einem anderen Zeichenfolge ist, die tragbare POSIX-Lösung entfernen

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

oder noch kürzer zu verwenden, ffmpeg -i "$i" "${i%4}3".

Die endgültige Lösung für diese Konstrukte ist das Kapitel Parameter Expansion for the POSIX shell.