In Bash führe ich aus:
alias myalias='echo foo
echo bar
echo baz'
myalias
was zurückgibt:
foo
bar
baz
Aber:
ssh localhost "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias"
Kehrt zurück:
foo
Warum?
Antwort1
Ich glaube, Sie haben einen Fehler in Bash gefunden. Dieser Fehler betrifft nur die Option -c
.
Die Remote-Ausführung hat nichts mit Ihrem Problem mit dem mehrzeiligen Alias zu tun. Sie können es in Ihrer lokalen Bash versuchen. Aber nicht in einem Bash-Skript oder einer interaktiven Bash. Versuchen Sie es mit einer -c
Option wie dieser
bash -c "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias"
Dieselbe Ausgabe wie bei Ihrem Problem. Es foo
wird nur gedruckt.
Um die richtige (erwartete) Ausgabe zu erhalten, müssen Sie, myalias
wie @cuonglm vorgeschlagen hat, mindestens eine weitere Zeile nach hinzufügen.
bash -c "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias
:"
Warum sollte das so passieren? Warum myalias
hilft eine weitere Zeile danach?
Ich möchte nur sagen, dass das keinen Sinn ergibt. Kein Dokument in Bash erklärt oder erwähnt diesen Fall, nicht im Geringsten. Es sollte nicht so laufen. Das ist ein Fehler. Nachdem Sie den Code gelesen haben, werden Sie sich dessen sicher sein.
Gehen Sie zurück zum ersten problematischen Befehl. Ändern Sie diesmal nichts, sondern kompilieren Sie Bash einfach neumit "ONESHOT" undefined, dann erhalten Sie die richtige (erwartete) Ausgabe. Ja, Sie haben richtig gehört, der Befehl hat zwei verschiedene Verhaltensweisen, nur aufgrund unterschiedlicher Konfigurationen zur Kompilierungszeit.
Ob definiert ONESHOT
oder nicht, führt zu zwei völlig unterschiedlichen Routen im Bash-Code -c "command"
. Wenn ONESHOT nicht definiert wird, -c "command"
wird die normale Coderoute ausgeführt, die die Coderoute für fast alle Bash-Ausführungen ist, wie z. B. interaktive Befehle und Bash-Skripte. Wenn ONESHOT jedoch definiert wird, -c "command"
wird eine andere spezielle Route ausgeführt, die speziell nur dafür entwickelt wurde, um die Leistung durch Vermeidung von Forks zu verbessern.
In diesem Fall kann die normale und meistverwendete Methode die richtige Ausgabe liefern, während die spezielle Methode dies nicht kann. Ich denke, das inkonsistente Verhalten ist nicht das, was die Bash-Autoren wollen. Was das richtige Verhalten angeht, neige ich dazu, zu glauben, dass die normale Methode richtig ist.
Einige Details zu diesem Fehler
Der folgende Codeabschnitt ist mit dem Fehler verbunden. Er stammt aus der Funktion parse_and_execute() in der Datei builtins/evalstring.c
while (*(bash_input.location.string))
{
...
}
Diese while
Schleife wird zeilenweise ausgeführt, wobei in einer Schleife jeweils eine Zeile verarbeitet wird. Nach dem Lesen myalias
der letzten Zeile im Befehl (siehe oben) while
wird die Bedingung falsch. myalias
wird auf drei Echozeilen erweitert, aber in dieser Schleife wird nur ein Echo verarbeitet; die beiden anderen Echos werden in der nächsten Schleife verarbeitet, aber... es gibt keine weitere Schleife.
Wenn Sie myalias
nach read eine weitere Zeile hinzufügen, bleibt myalias
die Bedingung in erfüllt, sodass die beiden anderen Echos die Chance erhalten, in der nächsten Schleife ausgeführt zu werden. Die letzte Zeile danach wird verarbeitet, nachdem alle durch erweiterten Echos verarbeitet wurden.while
myalias
myalias
AKTUALISIEREN
Ich habe vergessen, die Version von Bash anzugeben, die von diesem Problem betroffen ist.
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
Antwort2
Problemumgehung (inspiriert von @cuonglm):
ssh localhost "shopt -s expand_aliases &>/dev/null;
alias myalias='ls
echo foo
echo bar
echo baz'
myalias &&
true"
Dadurch bleibt der Exit-Code erhalten. Das true
, jedochmussin einer neuen Zeile stehen.
Es ist immer noch nicht erklärt, warum. Aber es sieht immer mehr nach einem Fehler aus.