3

In Bash I run:

alias myalias='echo foo
echo bar
echo baz'
myalias

which returns:

foo
bar
baz

But:

ssh localhost "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias"

Returns:

foo

Why?

  • 1
    if you add echo after running myalias, the it works. See the debug3 log in both server and client, you will see that server read failed so it signal eof to client. I'm not sure why running alias alone cause it. Maybe the server see there's no command after myalias so it signal eof right after calling myalias. – cuonglm Nov 25 '17 at 15:13
  • 1
    @cuonglm, why server sends eof to client, because "bash -c" exits. And the "bash -c" exits without running the rest echos expanded from "myalias" , which I think is bug. I've posted an answer to discuss this issue. – Bruce Nov 26 '17 at 2:34
  • @Bruce Ah yes, I wonder you or Ole Tange have sent bug report to bash bug mail list? – cuonglm Nov 27 '17 at 4:49
  • I vote for @Bruce sending it: He clearly has a better understanding of the inner workings than I do. – Ole Tange Nov 27 '17 at 8:12
  • I intend to, when I have time. – Bruce Nov 28 '17 at 1:33
4

I think you found a bug of Bash. This bug is specific to option -c.

Remote running has nothing to do with your problem about the multi-line alias. You can try it in your local bash. But not in bash script or interactive bash, try it with -c option, like this

bash -c "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias"

Same output as your problem. Only foo is printed.

To get the right (expected) output, you have to at least add one more line after myalias, as @cuonglm suggested.

bash -c "shopt -s expand_aliases &>/dev/null;
alias myalias='echo foo
echo bar
echo baz'
myalias
:"

Why would it happen this way? Why does one more line after myalias help?

I just want to say that this doesn't make sense. No document in Bash explains or mentions this case, not a little bit. It is not supposed to run this way. This is a bug. After reading code, you'll make sure of this point.

Go back to the first problematic command. This time don't change anything, just re-compile bash with "ONESHOT" undefined, then you'll get right (expected) output. Yes, you hear right, the command has two different behaviors just because of different compile-time config.

Whether define ONESHOT or not will lead to two completely different route in Bash code for -c "command". If undefine ONESHOT, -c "command" will run the normal code route, which is the code route for almost all bash executions, such as interactive command and bash script. But if define ONESHOT, -c "command" will run another particular route which is specially designed for it only, to improve its performance by avoiding fork.

For this case, the normal and mostly used way can give right output, while the particular way can't. I think the inconsistent behavior is not what the Bash authors want. As to which behavior is right, I tend to think the normal way is right.

Some details about this bug

The following piece of code is related to the bug. It is from function parse_and_execute() in file builtins/evalstring.c

while (*(bash_input.location.string))
  {
    ...
  }

This while loop will run by lines, handling one line in one loop. After read myalias, the last line, in the command (see above), the condition in while will become false. myalias is expanded to three lines of echo, but only one echo is handled in this loop; the two other echo will be handled in next loop, but... there is not another loop.

If you add one more line after myalias, after read myalias, the condition in while will remain true, so the two other echo will get chance to run in next loop. The last line after myalias will be handled after all echos expanded by myalias are handled.

UPDATE

I forgot to say the version of Bash involved in this issue, which is

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
1

Workaround (inspired by @cuonglm):

ssh localhost "shopt -s expand_aliases &>/dev/null;
alias myalias='ls
echo foo
echo bar
echo baz'
myalias &&
true"

This will preserve exit code. The true, however, must be on a new line.

It still does not explain why. But it more and more looks like a bug.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.