Ble.sh: [履歴] 複数行の履歴展開が不本意な挙動を示す

Created on 9 Jul 2019  ·  9Comments  ·  Source: akinomyoga/ble.sh

ble version: 0.4.0-devel1+30cc31c
Bash version: 5.0.3(1)-release

複数行のコマンドを実行した後に履歴展開を行うと,展開の対象が(その複数行のコマンドではなく)evalから始まる単一行になってしまい,例えば単語指示子などが本意に展開されません。
実行例

$ echo '複数行の' \
'入力'
$ echo !^ # ここでは第1引数,つまり「`'複数行の'`」に展開されることを期待している
$ echo -- # しかし実際は「`--`」に展開されてしまう。

直前のコマンド(全体)を展開する「!!」は「eval -- $'echo 複数行の \\n入力'」に展開されます。

NYNewFeat

All 9 comments

ありがとうございます。これは既知の問題で、対応を諦めていたものでした。

現在、ble.sh では《複数行コマンドをそのままの形で .bash_history に記録したい》という動機から Bash history に記録する時に、複数行コマンドを eval -- $'' の形式に変換しています。ble.sh が Bash history から取り出す時に、eval -- $'' を再び複数行に復号することで ble.sh は表面上は複数行コマンドを .bash_history に記録できている様に振る舞っている訳です。しかし、履歴展開は Bash history の上で動作するので eval -- $'' の形式をそのまま使ってしまうのです。

現実的な速度で動作する対処法がないか少し考えてみることにします。或いは、複数行コマンドを形を保つように .bash_history に記録するのを諦めるという手もあります [ Note: shopt -s lithist の時は複数行コマンドがそのまま .bash_history に記録されますが、読み出す時には各行が分断されて読み出されてしまいます。また、shopt -u lithist の時はセミコロン区切りに変換されるので、各行が分断されることはありませんが読みにくくなってしまいます ]。

既知の問題なのですね,確認不足で失礼しました……。


ところで,複数行のコマンドの履歴展開ができる「_非_ 現実的な速度で動作する」対処法というのは,例えば各引数を履歴番号と連動した配列引数に収めるとかそういう類のものでしょうか?

確認不足ということはないです (既知といっても私が認識しているというだけの話なので…)。このように要望を出していただければ対応の優先順位をつけることができて助かるので、どうぞ余り気になさらずに報告をしていただければ幸いです。

取り敢えずの案は:

  • (a) 自前で履歴展開を実装?

    例えば各引数を履歴番号と連動した配列引数に収めるとかそういう類のものでしょうか?

    確かにそのようにして ble.sh で自前で履歴展開を実装するという手もありますね。しかし、面倒だし今の所余り意義を見出だせないので、気が進みません。

  • (b) "現実的な速度" と書いたのは以下のような遅そうな方法が念頭にあった為でした:

    • Bash history にはそのまま改行を含めて登録する。
    • .bash_history への書き込みは eval -- $'' の形式で自前で行う。
    • 起動時に .bash_history から Bash history への読み出しを自前で処理する。

    というものですが、最後の部分が遅そうです。


8cf17f7 対応しました。やってみればできるものですね…

ありがとうございます!
ただ,非常に細かいことなのですが,Bash (5.0)と少し違う履歴展開の挙動があるように思えます。私の個人的な感性ではあるのですが,Bashの挙動のほうが〝自然〟でしたので,できればBash側に合わせていただきたいです。

具体的には最後の引数を展開する単語指示子を用いた場合,Bashでは真に最後の引数のみが展開されるのに対し,ble.shではそうではありません。
例えば次のような場合の挙動に差異があります。

$ echo $BLE_VERSION # `ble.sh`だと
0.4.0-devel1+bf6bd23
$ echo 複数行の \
入力
$ echo !$
$ echo \
入力
^^^^^^^^ こうなる。
$ bash --norc # 素のBashだと
$ echo 複数行の \
入力
$ echo !$
$ echo 入力
  ^^^^^^^^^ こう。

早速試していただきありがとうございます!

うーん。色々試してみました。ble.sh では入力した物をそのまま履歴に登録しているのですが、そもそも Bash だと行末の \ を消去してから履歴に登録するようですね…。

ble.sh$ echo A \
B
A B
ble.sh$ history 2
 5740  echo A \
B
 5741  history 2
ble.sh$ bash --norc
bash$ echo A \
> B
A B
$ history 2
 5804  echo A B
 5805  history 2
bash$

この動作は lithist に関係なく行われる様です。

bash$ shopt -s lithist
bash$ echo C \
> D
C D
bash$ history 2
 5807  echo C D
 5808  history 2

しかし、行末以外のクォートは除去しない様です。

bash$ echo A\B
AB
bash$ history 2
 5809  echo A\B
 5810  history 2
bash$ exit

一つの手は bash と同様に行末の \ + LF を除去してから履歴に登録するというものですが、個人的には \ を使うのは横に長くなったコマンドラインを見やすくする為なので、\ も含めて履歴に登録してくれた方がありがたい気がします。其処はどのように思われますか。例えば、以下のように (好い加減な例ですが) 現状の ble.sh の振る舞いの方がありがたいと思うのです。

ble.sh$ echo hello world | \
sed 's/hello/Hello/;s/world/World/' | \
sed 's/Hello/HELLO/;s/World/WORLD/' | \
awk '{print $2}'
WORLD
ble.sh$ echo hello world | \
sed 's/hello/Hello/;s/world/World/' | \
sed 's/Hello/HELLO/;s/World/WORLD/' | \
awk '{print $2}'
ble.sh$
ble.sh$ bash --norc
bash$ echo hello world | \
> sed 's/hello/Hello/;s/world/World/' | \
> sed 's/Hello/HELLO/;s/World/WORLD/' | \
> awk '{print $2}'
bash$ echo hello world | sed 's/hello/Hello/;s/world/World/' | sed 's/Hello/HELLO/;s/World/WORLD/' | awk '{print $2}'

なるほど仰る通り,そもそも「なぜ複数行のコマンドラインが(少なくとも利用者にとって)必要なのか」を考えたとき,複数行のコマンドの最後の引数は\+改行であった方が有用性が高いですね。
私が試したコマンドラインの例では,そもそも一つの行の長さが(複数行コマンドの例として持ち出すには)短かすぎて,その考えに至りませんでした。
履歴の展開には\+改行を付けたままでお願いします。

折角提案くださったのですが対応できずすみません (書き忘れていましたが結局 (b) の方策で対応しましたので履歴展開は Bash のそれを使っています。なので履歴に \+改行 が含まれている限りは \改行文字列 が最後の単語として切り出されてしまうという振る舞いを変えるのは難しそうです)。

取り敢えず閉じますね。ありがとうございました!

Was this page helpful?
0 / 5 - 0 ratings