akinomyoga/ble.sh

BLE_PIPESTATUS doesn't appear to update until after PROMPT_COMMAND is called

Closed this issue · 5 comments

ble version: 0.4.0-devel4+063249b
Bash version: 5.2.32(1)-release (aarch64-apple-darwin23.4.0)

$ ble/widget/display-shell-version
GNU bash, version 5.2.32(1)-release (aarch64-apple-darwin23.4.0)
ble.sh, version 0.4.0-devel4+063249b (noarch) [git 2.46.0, GNU Make 3.81, GNU Awk 5.3.0, API 4.0, (GNU MPFR 4.2.1, GNU MP 6.3.0)]
locale: LANG=en_US.UTF-8
terminal: TERM=xterm-256color wcwidth=15.0-west/15.1-2+ri, wezterm:20220408 (1;277;0)
options: +inherit_errexit

I would like to use a prompt that shows PIPESTATUS. I see that ble.sh provides it's own version of PIPESTATUS called BLE_PIPESTATUS - no problem! I don't mind using that over PIPESTATUS. However, BLE_PIPESTATUS does not appear to be properly updated before PROMPT_COMMAND is called, which makes it difficult to use in a prompt and appears to be different behavior than Bash't built-in PIPESTATUS. Here's a demo of the problem:

Bash default example

.bashrc contents

#!/usr/bin/env bash
# contents of minimal ~/.bashrc
function test_pipestatus() {
  echo "PIPESTATUS: ${PIPESTATUS[@]}"
}
PROMPT_COMMAND="test_pipestatus"

behavior

PIPESTATUS: 0
bash-5.2$ true | false | true
PIPESTATUS: 0 1 0
bash-5.2$ true | false
PIPESTATUS: 0 1
bash-5.2$

Notice that the PIPESTATUS array is filled and reports its contents on every call to test_pipestatus.

Now the ble.sh example

.bashrc contents

#!/usr/bin/env bash
# contents of minimal ~/.bashrc
[[ $- == *i* ]] && source "$HOME/.local/share/blesh/ble.sh" --noattach
function test_pipestatus() {
  echo "BLE_PIPESTATUS: ${BLE_PIPESTATUS[@]}"
}
PROMPT_COMMAND="test_pipestatus"
[[ ${BLE_VERSION-} ]] && ble-attach

ble.sh behavior

BLE_PIPESTATUS:
bash-5.2$ true | false | true
BLE_PIPESTATUS:
bash-5.2$ true | false
[ble: exit 1]
BLE_PIPESTATUS: 0 1 0
bash-5.2$

Notice the BLE_PIPESTATUS lags one command behind in its exit code reporting.

ble.sh example with restored PIPESTATUS

For completeness, we can also look at the behavior with exec_restore_pipestatus=1. It looks like this does not actually properly restore PIPESTATUS to the built-in behavior:

.bashrc contents

#!/usr/bin/env bash
# contents of minimal ~/.bashrc
[[ $- == *i* ]] && source "$HOME/.local/share/blesh/ble.sh" --noattach
function test_pipestatus() {
  echo "PIPESTATUS: ${PIPESTATUS[@]}, BLE_PIPESTATUS: ${BLE_PIPESTATUS[@]}"
}
PROMPT_COMMAND="test_pipestatus"
bleopt exec_restore_pipestatus=1  # restores PIPESTATUS
[[ ${BLE_VERSION-} ]] && ble-attach

ble.sh behavior with restored PIPESTATUS

PIPESTATUS: 0, BLE_PIPESTATUS:
bash-5.2$ true | false | true
PIPESTATUS: 0, BLE_PIPESTATUS:
bash-5.2$ true | false
[ble: exit 1]
PIPESTATUS: 1, BLE_PIPESTATUS: 0 1 0
bash-5.2$ true | false | true | true
PIPESTATUS: 0, BLE_PIPESTATUS: 0 1
bash-5.2$

Notice in this example, PIPESTATUS is restored, but never has more than one element.

I have really appreciated all the help and support Koichi. I have been really loving this project. Let me know if I'm doing something wrong or if there's an easy fix.

BLE_PIPESTATUS is intended to be only set for the user command, i.e., the command that is input and run in the command line. PROMPT_COMMAND is not the target of BLE_PIPESTATUS, but I can expose it also for PROMPT_COMMAND.

I'm open to whatever makes the most sense here. I know PIPESTATUS is essentially a magic variable and Bash makes it difficult to do things with it, so if BLE_PIPESTATUS or some other method is preferable, I'm open to whatever you think is best.

I was surprised in that last example that using bleopt exec_restore_pipestatus=1 didn't really restore PIPESTATUS to it's pre-blesh behavior. Is that even possible to do?

While looking at the code, I noticed that my thought when I implemented BLE_PIPESTATUS was that BLE_PIPESTATUS should be available in blehook PRECMD but not in PROMPT_COMMAND (blehook PRECMD is ble's way of PROMPT_COMMAND) because BLE_PIPESTATUS is the ble-specific feature and the code using BLE_PIPESTATUS should go into PRECMD instead of PROMPT_COMMAND. But anyway I can provide BLE_PIPESTATUS also to PROMPT_COMMAND.

I was surprised in that last example that using bleopt exec_restore_pipestatus=1 didn't really restore PIPESTATUS to it's pre-blesh behavior.

The target of bleopt exec_restore_pipestatus=1 is also only user commands. Currently, PIPESTATUS is not restored for the commands in PROMPT_COMMAND even if you set bleopt exec_restore_pipestatus=1.

ble.sh behavior with restored PIPESTATUS

PIPESTATUS: 0, BLE_PIPESTATUS:
bash-5.2$ true | false | true
PIPESTATUS: 0, BLE_PIPESTATUS:
bash-5.2$ true | false
[ble: exit 1]
PIPESTATUS: 1, BLE_PIPESTATUS: 0 1 0
bash-5.2$ true | false | true | true
PIPESTATUS: 0, BLE_PIPESTATUS: 0 1
bash-5.2$

So the above is the expected behavior of the current version. What bleopt exec_restore_pipestatus=1 provides is this:

$ bleopt exec_restore_pipestatus= # this is default
$ true | false | true
$ declare -p PIPESTATUS
declare -a PIPESTATUS=([0]="0")
$ bleopt exec_restore_pipestatus=1
$ true | false | true
$ declare -p PIPESTATUS
declare -a PIPESTATUS=([0]="0" [1]="1" [2]="0")

Is that even possible to do?

ble.sh uses a stupid way:

ble.sh/src/edit.sh

Lines 7392 to 7398 in 063249b

if [[ $bleopt_exec_restore_pipestatus ]] && ((${#BLE_PIPESTATUS[@]} > 0)); then
local i pipe=
for ((i=0;i<${#BLE_PIPESTATUS[@]};i++)); do
pipe=$pipe'| (exit '${BLE_PIPESTATUS[i]}')'
done
_ble_edit_exec_BASH_COMMAND_eval="${pipe:2}; $_ble_edit_exec_BASH_COMMAND_eval"
fi

It inserts a dummy command (exit "${_saved_pipestus[0]}") | ... | (exit "${_saved_pipestus[n]}") before the user command. This is the reason that I don't want to enable it by default.

I implemented them in commit 2788883.

WOW 🥇!

I can confirm, after running ble-update, this works as expected. Thank you so much!