1

Can someone please explain this to me:

$ type blah
bash: type: blah: not found
$ echo $?
1

$ bash --init-file <( echo "type blah; echo $?" )
bash: type: blah: not found
0

Why is the return value different in an init script than it is in an interactive shell?

Edit And different again in the init script for an interactive shell:

$ bash --init-file <( echo "type blah; echo $?" ) -i
bash: type: blah: not found
2

Bash version is 4.4.12.

2 Answers 2

1

The problem is that $? is being evaluated before it's passed as an argument to the outer echo command. As a result, you're running the equivalent of:

bash --init-file <( echo "type blah; echo 0" ) -i

You can sort of see this using set -x mode which'll show the equivalent of the executed commands:

$ bash --init-file <( echo "type blah; echo $?" )
+ bash --init-file /dev/fd/63
++ echo 'type blah; echo 0'
bash: type: blah: not found
0

Note the third line, ++ echo 'type blah; echo 0' -- the $? has already been expanded before it's passed to the echo command (and thence to the new shell to execute). BTW, this means that it's actually showing the result of the last command run before this one:

$ curl http://notarealdomain.example.com/
curl: (6) Could not resolve host: notarealdomain.example.com
$ bash --init-file <( echo "type blah; echo $?" )
bash: type: blah: not found
6

Note that the status printed, "6", is curl's code for an unresolvable host error.

Anyway, the solution is simple: use single-quotes to delay evaluation of the $?:

$ bash --init-file <( echo 'type blah; echo $?' )
bash: type: blah: not found
1
1
  • Oh, FFS, sorry for a stupid question!
    – Tom
    Nov 21, 2017 at 11:41
1

Answer first

  1. The init-script does nothing to do with the return value.
  2. The process substitution <(...) is the real cause.

Explanation

[Proof1] Now try this command below. It has no option --init-file, but same output (with some trivial prints).

(exit 10)
bash <( echo "type blah; echo $?" )

[Proof2] Try following commands, and change the number in first one, you will find more different return value.

(exit 117)
bash --init-file <( echo "type blah; echo $?" )

[Proof3] What output do you see?

(exit 23)
cat <( echo "type blah; echo $?" )

I think you can understand Answer1 via the 3 proofs above. What about Answer2?

If you just type the simple command echo "type blah; echo $?" to run it, what result do you expect? You expect it will print type blah; echo <num>, of which the <num> is the return value of the last command. Right? We all know the $? will be replaced with a number, because we all know bash will perform parameter expansion on $?.

It is same in process substitution <(...).

In Bash, commands which appear in a process substitution <(...) will be invoked in a subshell. Subshell is also a shell(Bash). So for the command echo "type blah; echo $?", you will get the exactly same result in subshell as you run it straightforward in current shell, which means subshell will also perform parameter expansion on $?, just like what a bash does.

So when you are running bash --init-file <(echo "type blah; echo $?"), the content of the init-script file is actually type blah; echo <num> rather than type blah; echo $?.

Deeper

Although in process substitution <(echo "type blah; echo $?") the $? is expanded by subshell, its value is affected by current shell. The value of ? in subshell is as same as the one in current shell.

See the Proof2, the first command is (exit 117), which makes the value of $? in current shell be 117. But it is expanded to 117 in subshell too.

This is because the commands appearing in process substitution <(...) are invoked in a subshell environment that is a duplicate of the current shell environment.

So this explains in your question you showed different return value if you put option -i in the command. That is not the difference brought by option -i. That is because before that command, you definitely execute another command which returns 2.

More words

In fact, there are three other forms working the same way as process substitution in bash.

  1. Command substitution $(...) or `...`
  2. Commands grouped with parentheses (...)
  3. Builtin commands in pipeline <builtin cmd> | ... or ... | <builtin cmd> or <builtin cmd> | <builtin cmd>

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .