Skip to content

Commit

Permalink
edit: work around a bash-4.4..5.1 bug of "exit" outputting time to st…
Browse files Browse the repository at this point in the history
…derr of exit context
  • Loading branch information
akinomyoga committed Feb 13, 2022
1 parent b27f758 commit 3de751e
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/ChangeLog.md
Expand Up @@ -90,6 +90,7 @@
- complete (menu-style:align): refactor `complete_menu_align => menu_align_{min,max}` (motivated by banoris) `#D1717` 22a2449
- prompt: support `bleopt prompt_command_changes_layout` `#D1750` e199bee
- exec: measure execution times `#D1756` 2b28bec
- edit: work around a bash-4.4..5.1 bug of `exit` outputting time to stderr of exit context `#D1765` 0000000

## Changes

Expand Down
96 changes: 96 additions & 0 deletions note.txt
Expand Up @@ -1780,6 +1780,27 @@ bash_tips
実はロード時刻は $_ble_base_run/$$.load に既に存在しているのでそれを使えば
良い。

検索してみると結構影響のありそうな箇所は沢山ある。一方で具体的にどの様に直
すのか。load time だけで判定すると毎回キャッシュを更新する事になってしまっ
て意味がない。(キャッシュが最新でない) & (load time よりも前に元のファイル
が更新されている) という条件で判定するのが良い気がする。

* キャッシュが最新でないというのは必須の条件である。そうでなければそもそも
更新を実行する意味がない。load time が元ファイルより最近の場合にも何も問
題は生じないので今迄通りに何も気にせずに更新すれば良い。

* 従って、特に判断が難しいのは (キャッシュ),(LoadTime) > (元ファイル) の場
合である。更に元ファイルを source した時刻は実は元ファイルよりも後の可能
性もある。その時にはメモリ上には最新版があるのでそれに従ってキャッシュを
更新しても問題ない。

* よく考えたらここで対策をしても問題は生じるのではないか。元々キャッシュファ
イルが存在しなくて、メモリ上に古い version の関数があって、それに基づいて
新しくキャッシュを作成した場合、古い version のキャッシュがずっと残ってし
まう。

うーん。load time で time stamp を書き換える様にすれば良いのだろうか。

* complete: 補完している時に勝手に変数の中身が展開されてしまう事がある。展開
する条件をより制限する事はできないか。或いは、展開せずに置換できるパターン
を増やす。
Expand Down Expand Up @@ -6026,6 +6047,81 @@ bash_tips
Done (実装ログ)
-------------------------------------------------------------------------------

2022-02-09

* edit: exit を実行すると時間計測結果が表示されてしまう [#D1765]

これは何? 恐らく最近のコマンド時間計測と関連しているのだと思うが。そもそも
exit を実行した後に time の結果が表示されうる物なのだろうか。

うーん。以下を実行すると時間が実際に表示される。

time { echo hello; exit; }

以下を実行した場合には特に何も表示されない (exit と単に表示される)。

time { echo hello; TIMEFORMAT=''; exit; }

少なくとも workaround として TIMEFORMAT= は有効であるという事。
以下の様にした場合でもちゃんと出力は抑制される。

function f1 { local TIMEFORMAT=''; exit; }; time f1

然し、この時の問題はユーザーが ble.sh の中で time { ... exit; } を実行した
時に時間表示がされなくなってしまうという事。という事を考えると、やはり外側
で設定する時に何か工夫して外で設定した time に関しては時間測定結果が表示さ
れない様に修正するという事。

うーん。然し、何故これが表示されてしまうのか。本来は別の所に出力されるべき
なのではないか。。。これは bash に報告するべきなのではないか。

* 取り敢えずおかしな現象が起こるという事をもっと簡単な場合で証明する事は可
能だろうか。

実際に振る舞いとしておかしいという事も以下のコマンドで確認する事ができる。
つまり time の出力は 2>/dev/null に吸い込まれるべきであるのにも拘らず、
exit が実行された文脈での 2 の接続先である 2>/dev/tty に出力されてしまう。

$ bash-5.1 --norc
$ function f1 { { echo hello; exit; } 2>/dev/tty; }
$ { time f1; } 2>/dev/null

* →と思ったらどうやら 5.2 では既に修正されている様だ。更に昔の version の
振る舞いも確認してみた所、どうやら 4.4..5.1 で発生する問題の様である。

これに関しては exit の側で workaround をする事にする。これによって 4.4..5.1
では、ユーザーが time { echo hello; exit; } を実行した時に何も表示されなく
なるが、それは仕方がない。

そもそも 4.3 以前では time { echo hello; exit; } に対して結果は出力されなかっ
た様である。という事を思うと、実は 4.4..5.1 は time の内部で exit を実行し
た場合でも時間を表示する様に修正する上での壊れた振る舞いだったという事にな
る。

x 2022-02-13 動いていない。#D1771 をテストする時にで時間計測の結果が出力さ
れてしまう問題が未だ残存している事に気づいた。うーん。既に対策した筈だが
動いていないという事。

うーん。改めて試してみたが確かに全然動いていない。

? reject: うーん。つまり… trap handler の側の stderr に出力されていると
いう事だろうか。EXIT trap handler は ble.sh は常に設定している。

一応 EXIT trap handler が呼び出されなかった・設定が消えている場合に備え
る為に ble/builitin/exit での対策は残しておく事にする。

と思って trap handler の側で TIMEFORMAT= にしたが効かない。というより
trap handler が呼び出されるよりも前に時間測定結果が表示されている。因み
に EXIT handler はトップレベルで呼び出されている。

うーん。global の TIMEFORMAT を空にしたら表示されなくなった。取り敢えずは
それで実行する事にする。と思ったが…うーん。exit に失敗したら元に戻さなけ
ればならないのでは。因みに global の値を取得するのは少々面倒である。

うーん。後 global の TIMEFORMAT を unset する手段がない。取り敢えず既定値
を代入すれば良いのだろうか。既定値は man bash にあったのでそれをそのまま
使う事にした。

2022-02-05

* [棄却] 2022-01-31 nix-build -A の補完が効かないという問題 [#D1764]
Expand Down
31 changes: 31 additions & 0 deletions src/edit.sh
Expand Up @@ -5429,10 +5429,41 @@ function ble/builtin/exit {
done
fi

# Note #D1765: Bash 4.4..5.1 では "{ time { exit 2>/dev/tty; } } 2>/dev/null"
# に対して、time の時間計測結果を 2>/dev/null ではなくて 2>/dev/tty に出力
# してしまうバグがある。その為に ble/exec/time の計測に使用している time の
# 出力が画面に表示されてしまう。仕方がないので time の出力を空の TIMEFORMAT
# により抑制する。抑々 4.3 以前では exit を実行した時に外側の time の測定も
# 全てキャンセルされていたので time を握り潰しても 4.3 以前の振る舞いに戻る
# だけなので気にしない事にする。
# Note #D1765: 手元の実験では local TIMEFORMAT= だけ指定していれば問題は発生
# しなかったが、実際に ble.sh に実装してみると global TIMEFORMAT を指定しな
# ければ抑制できなかったので、global TIMEFORMAT を一時的に書き換える。
if ((40400<=_ble_bash&&_ble_bash<50200)); then
# TIMEFORMAT の値の保存
local global_TIMEFORMAT local_TIMEFORMAT
ble/util/assign global_TIMEFORMAT 'ble/util/print-global-definitions TIMEFORMAT'
if [[ $global_TIMEFORMAT == 'declare TIMEFORMAT; builtin unset -v TIMEFORMAT' ]]; then
global_TIMEFORMAT='declare TIMEFORMAT=$'\''\nreal\t%3lR\nuser\t%3lU\nsys %3lS'\'
else
global_TIMEFORMAT="declare -g ${global_TIMEFORMAT#declare }"
fi
ble/variable#copy-state TIMEFORMAT local_TIMEFORMAT

declare -g TIMEFORMAT=
TIMEFORMAT=
fi

ble/util/print "${_ble_term_setaf[12]}[ble: exit]$_ble_term_sgr0" >&2
ble/base/.restore-bash-options set shopt
builtin exit "${opt_args[@]}" &>/dev/null
builtin exit "${opt_args[@]}" &>/dev/null

# exit に失敗した時はできるだけ元の状態に戻す
if ((40400<=_ble_bash&&_ble_bash<50200)); then
builtin eval -- "$global_TIMEFORMAT"
ble/variable#copy-state local_TIMEFORMAT TIMEFORMAT
fi
return 1 # exit できなかった場合は 1 らしい
}

Expand Down

0 comments on commit 3de751e

Please sign in to comment.