Der Bash-Befehl in der Zeichenfolge wird ausgeführt, wenn ich die Zeichenfolge erstelle, nicht, wenn ich sie später verwende

10

Ich bin relativ neu in der Shell-Skripterstellung, habe aber fast ein Skript fertiggestellt, das das lftp-Programm verwendet . Der Teil des Skripts, mit dem ich Probleme habe, besteht darin, eine lange Befehlsfolge (getrennt durch ;) zu erstellen .

for var in something
do
    ...
    commands_to_run+="echo Result is `tail -n 1 $somefile`;"
done

Was ich tailbemerke , ist, dass das Programm - in die Backticks eingeschlossen - ausgeführt wird, wenn die for-Schleife iteriert, aber nicht, wenn ich die Befehlsfolge später in meinem Skript aufrufe.

Leider kann der Inhalt von $ somefile derzeit nicht überprüft werden. Wie kann ich den Befehl ausführen lassen, wenn ich ihn brauche und nicht, während ich den String erstelle?

Ricky
quelle

Antworten:

8

Dieser ist ein bisschen knifflig. Die Informationen, die Hauke ​​bereitgestellt hat, sind korrekt. Es geht nur darum, sie für Ihren Anwendungsfall zu analysieren.

Am einfachsten ist es, die $()Syntax $so zu verwenden, dass die Variablendefinition den $()zum Zeitpunkt der Definition enthaltenen Befehl nicht ausführt . Die Einschränkung besteht darin, dass das Endergebnis evalzum Zeitpunkt der tatsächlichen Ausführung von der Shell (über ) neu ausgewertet werden muss, damit der verschachtelte Befehl ausgeführt werden kann.

Es ist viel einfacher, sich ein Beispiel anzusehen. Nehmen Sie also dieses, das Sie auf den richtigen Weg bringen sollte:

for((i=0;i<10;i++)); do 
  date +%s.%N  # Print a timestamp (in format seconds.nanoseconds)
  test="echo \$(date +%s.%N)" # Save a command to do the same
  sleep 1      # Sleep for a second
  eval "$test" # Evaluate the command saved in variable 'test'
  echo         # Print a new line before the next iteration
done

Hier ist eine Beispielausgabe aus dem obigen Beispiel (auf eine Iteration gekürzt):

1398832186.133661344
1398832187.139076728

Sie werden feststellen, dass der zweite Zeitstempel für jede Schleife etwa eine Sekunde nach dem ersten liegt. Wenn Sie dagegen denselben Test durchführen, ohne sich $der testDefinition zu entziehen und die zu entfernen eval, stimmen die beiden Zeitstempel nahezu überein.

Gewöhnen Sie sich nicht an, in den evalmeisten Situationen zu verwenden, aber dies ist einer von denen, bei denen ich keinen guten Weg kenne, dies zu vermeiden. Hoffentlich hilft das. Viel Glück!

daBeamer
quelle
Vielen Dank, ich habe versucht, $(...)wie von Hauke ​​vorgeschlagen, aber der Backslash ist der Schlüssel.
Ricky
Ich evalbin froh, dass es geholfen hat - denken Sie jedoch daran, dass der Schlüssel hier wirklich darin besteht, dass Sie dasselbe tun können, indem Sie dem Befehl nicht entkommen $und einfache Anführungszeichen ( ') anstelle von doppelten Anführungszeichen ( ") verwenden, um Ihren Befehl zu umgeben.
daBeamer
Jetzt habe ich gerade festgestellt, dass das Echo, genau wie bei Huakes Vorschlägen, wenn ich dies im lftp-Programm verwende, den Befehl einfach druckt und ihn nicht wirklich ausführt. Möglicherweise müssen Sie ihre Mailingliste für spezifischere Hilfe versuchen.
Ricky
Welchen Befehl möchten Sie ausführen? Ich hatte den Eindruck, Sie wollten echoeine Zeichenfolge mit Inhalten, einschließlich der Ausgabe eines verschachtelten Befehls mit verzögerter Ausführung.
daBeamer
1
@ Ricky Ich müsste allen Punkten von @HaukeLaging zustimmen. Der Code wie er ist minus der echowird nicht funktionieren, da es keinen Befehl gibt eval, sondern eine Zeichenfolge. Wenn Sie ein passenderes Beispiel für uns haben, können wir versuchen, Ihnen zu helfen.
daBeamer
6

Es gibt verschiedene Zitierstufen. Doppelte Anführungszeichen ( "...") protect Leerzeichen und einige Sonderzeichen ( ~, &, |, ;, ...) aber nicht daran hindert , Parameter Expansion und Kommandosubstitution.

Sie benötigen entweder einfache Anführungszeichen ( ') oder einen Backslash vor den "gefährlichen" Zeichen.

Im Allgemeinen: Sie sollten die Verwendung $(tail ...)von Backticks in Betracht ziehen. Backticks sind der ältere Standard, aber wir sprechen von so alt, dass $()die meisten Menschen keine Probleme haben. Die neue Version ist einfacher zu lesen und kann verschachtelt werden. Geschweige denn die Formatierungsprobleme hier auf sx ...

Hauke ​​Laging
quelle
Danke für die schnelle Antwort Hauke. Leider führt das Ersetzen der Backticks durch die empfohlenen $(...)immer noch zum gleichen Ergebnis - die Shell führt dies aus, wenn meine Zeichenfolge definiert ist.
Ricky
@ Ricky Das waren keine alternativen Vorschläge. Sie sollen verwenden, $()aber Sie brauchen die einfachen Anführungszeichen trotzdem.
Hauke ​​Laging
Also wird keine Kombination dieser Charaktere das erreichen, wonach ich suche?
Ricky
@ Ricky Was ist so schwer zu verstehen über "Sie brauchen entweder einfache Anführungszeichen"? Sie versuchen es offensichtlich nicht einmal.
Hauke ​​Laging
Eigentlich habe ich das getan, aber wenn ich einfache Anführungszeichen verwende, druckt das Echo einfach alles in dieser Zeile als normale Zeichenfolge. Was ist so schwer daran, vielleicht ein Beispiel zu geben?
Ricky