zshでは、Home、End、Delはスクリプトの内部と外部で異なる動作をします。

zshでは、Home、End、Delはスクリプトの内部と外部で異なる動作をします。

多くの欲求不満の終わりに、最初はメインシェルではなくスクリプトに影響を与えるように$EDITOR設定されており、この初期状態を元に戻すことができるものは何もないように見えました。vim環境変数をオフにして空の文字列に設定すると、使用法はbindkey -eスクリプトに伝播されません。bindkey -vデフォルトのシェルを使用してモードを切り替えて再切り替えすると、すべてのキーが両方のモードで期待どおりに機能します。

スクリプト内では、viの編集はデフォルトのシェルと同じようにうまく機能しますが、実行後のbindkey -e入力は奇妙に機能します。後続のキー入力と印刷のみが妨げHomeられます。EndDel~

キーを押して送信する内容をテストし、各キーを押す前にCtrl+を追加すると、デフォルトのシェルに入りますが、Vスクリプトに入ります。だから、2つは異なって行動し、1つは同じですが、振る舞います。[OH^[OF^[[3~[[H^[[F^[[3~

もちろん、スクリプトの移植性のためにのみキーバインディングを定義する必要はありません。なぜ壊れるの?

Zshはv5.9で、スクリプトは次のようになります。

#!/usr/bin/zsh
bindkey -e
vared -p 'x: ' -c x

私はあらかじめ入力された提案で文字列を編集するためのヒントを提供したいと思います。他のEmacsシーケンスも機能しますが、技術的な知識がないユーザーには教えない方が最善です。

ベストアンサー1

Home、、、、、、、はデフォルトでzshキーマップ(emacs、vi-insert、vi-command)を持つキーボードとBindingを押したときに一意の文字または文字シーケンスを送信する端末のキーマップではありEndませInsertん。..、完全なリストは参照または配列)。DeletePageUpPageDownbindkey -l$keymaps

または、上記のファンクションキーまたはファンクションキーまたは矢印キー、またはと組み合わせるとバインディングはTabまったくありません(対応するキーの固有シーケンスを送信する端末の場合も同様)。BackspaceEscapeShiftControlAlt

UpDownLeftRight矢印キー()は、ほとんどの端末がこれらのキーに同じエスケープシーケンスを送信するため、デフォルトでバインドされています。これは、ターミナルと/またはターミナルにあるかどうかに応じて、ほぼ常に^[[A...^[[Dまたは^[OA...です。^[ODキーボード転送モード(参照smkxterminfo(5)そうではありません。

オプション(システムまたはユーザー初期化ファイルをスキップ(zshenvを除く))を使用して実行し、次を実行してさまざまなキーマップzshの主キーバインディングを表示できます。zsh-f

for m ($keymaps) bindkey -M $m | grep -H --label=$m .

手動また、キーマップのどのキーに対してどのemacsウィジェットが見つかったかが表示されます。vicmdviins

上記のループを上にパイプすると、次のgrep '\^\[[[O][A-D]'値に関係なくその内容が表示されます$TERM

% (for m ($keymaps) bindkey -M $m | grep -H --label=$m .) | grep '\^\[[[O][A-D]'
visual:"^[OA" up-line
visual:"^[OB" down-line
visual:"^[[A" up-line
visual:"^[[B" down-line
viopp:"^[OA" up-line
viopp:"^[OB" down-line
viopp:"^[[A" up-line
viopp:"^[[B" down-line
vicmd:"^[OA" up-line-or-history
vicmd:"^[OB" down-line-or-history
vicmd:"^[OC" vi-forward-char
vicmd:"^[OD" vi-backward-char
vicmd:"^[[A" up-line-or-history
vicmd:"^[[B" down-line-or-history
vicmd:"^[[C" vi-forward-char
vicmd:"^[[D" vi-backward-char
main:"^[OA" up-line-or-history
main:"^[OB" down-line-or-history
main:"^[OC" forward-char
main:"^[OD" backward-char
main:"^[[A" up-line-or-history
main:"^[[B" down-line-or-history
main:"^[[C" forward-char
main:"^[[D" backward-char
viins:"^[OA" up-line-or-history
viins:"^[OB" down-line-or-history
viins:"^[OC" vi-forward-char
viins:"^[OD" vi-backward-char
viins:"^[[A" up-line-or-history
viins:"^[[B" down-line-or-history
viins:"^[[C" vi-forward-char
viins:"^[[D" vi-backward-char
emacs:"^[OA" up-line-or-history
emacs:"^[OB" down-line-or-history
emacs:"^[OC" forward-char
emacs:"^[OD" backward-char
emacs:"^[[A" up-line-or-history
emacs:"^[[B" down-line-or-history
emacs:"^[[C" forward-char
emacs:"^[[D" backward-char

ほとんどのキーマップでは、この4つの矢印キーの2つのエスケープシーケンスは、特定の状況で通常実行されると予想される操作に関連付けられています。

これらのウィジェットはまた、一般的に使用されるemacs / viキー(たとえば、^Bemacsモードの^F、、、、^Pまたはvi-cmdモードの、、、、など)にバインドされます。^Nhjkl

もちろん、すべての端末が送信するよく知られている単一の制御文字である、およびへのバインディングもありますEscTabBSを送信する端末があり、DELを送信する端末がありますが)。BackspaceEnterBackspace

ただし、他のファンクションキーに関する情報は見つかりません。

たとえば、端末が押されたときに端末が送信する内容の概要を取得する方法があります。これはterminfoデータベースに知られていますEnd

$ (typeset -A count; for TERM (/usr/share/terminfo/*/*(.:t)) (( count[\$terminfo[kend]]++ )); typeset -p1 count)
typeset -A count=(
  [$'\M-\C-@O']=8
  ['']=1341
  [$'\M-\C-?\M-(']=6
  [$'\C-Ak\C-M']=1
  [$'\C-SI']=3
  [$'\C-[)4\C-M']=4
  [$'\C-[0']=8
  [$'\C-[F']=1
  [$'\C-[K']=4
  [$'\C-[OF']=99
  [$'\C-[T']=13
  [$'\C-[Y']=1
  [$'\C-[[146q']=23
  [$'\C-[[1~']=11
  [$'\C-[[220z']=21
  [$'\C-[[24;1H']=3
  [$'\C-[[4~']=139
  [$'\C-[[5~']=9
  [$'\C-[[8~']=28
  [$'\C-[[F']=49
  [$'\C-[[K']=1
  [$'\C-[[OF']=1
  [$'\C-[[U']=15
  [$'\C-[[Y']=16
  [$'\C-[[d']=1
  [$'\C-[_1\C-[\\']=1
  [$'\C-[k']=3
  [$'\C-[z']=13
  ['- @']=1
  ['-45~']=1
  ['-4~']=5
  [1!]=1
)

ほとんどの端末では、彼らが何をしているのかわかりません。$'\C-[[4~'最も一般的です。

Delete現在は通常送信されますが、\e[3~時にはDEL(^?)(最も一般的にはバックスペースキーで送信されます)が送信され、多くの人がそれをターミナルエミュレータ設定で設定できます。一部の端末エミュレータは、エミュレートするキーボードの種類を知らせ、そのファンクションキーに対して異なるシーケンスを送信します。たとえば、ドキュメントを引用したxtermを参照してください。

-kt キーボードタイプ
このオプションはkeyboardTypeリソースを設定します。可能な値は、「unknown」、「default」、「legacy」、「hp」、「sco」、「sun」、「tcap」、および「vt220」です。

これで、ユーザーはどのタイプのキーボードを使用するのか、どの端末エミュレータとどのシステムで使用するのかを知ることができます。オペレーティングシステムのベンダーはzshよりも正確な推測をすることができます(zshは30年以上にわたって数千のさまざまなシステムで使用されています)。

たとえば、x86 PC用のDebian GNU / Linuxディストリビューションは、パッケージに含まれている何十もの端末エミュレータ(ほとんど同様xterm)に合理的に忠実なterminfoデータベースを維持しようとします。通常、送信される複数のファンクションキーを知っています。 PCキーボードのキーボードは、ユーザーがターミナルエミュレータのデフォルト設定を変更せずに外部オペレーティングシステムからリモートでログインしない限り、使用できます。

/etc/zsh/zshrcしたがって、Debianはこれを(対話的に呼び出されるシステムカスタムファイル)に追加することに気付くでしょうzsh

# /etc/zsh/zshrc: system-wide .zshrc file for zsh(1).
#
# This file is sourced only for interactive shells. It
# should contain commands to set up aliases, functions,
# options, key bindings, etc.
#
# Global Order: zshenv, zprofile, zshrc, zlogin

READNULLCMD=${PAGER:-/usr/bin/pager}

# An array to note missing features to ease diagnosis in case of problems.
typeset -ga debian_missing_features

if [[ -z "${DEBIAN_PREVENT_KEYBOARD_CHANGES-}" ]] &&
   [[ "$TERM" != 'emacs' ]]
then

    typeset -A key
    key=(
        BackSpace  "${terminfo[kbs]}"
        Home       "${terminfo[khome]}"
        End        "${terminfo[kend]}"
        Insert     "${terminfo[kich1]}"
        Delete     "${terminfo[kdch1]}"
        Up         "${terminfo[kcuu1]}"
        Down       "${terminfo[kcud1]}"
        Left       "${terminfo[kcub1]}"
        Right      "${terminfo[kcuf1]}"
        PageUp     "${terminfo[kpp]}"
        PageDown   "${terminfo[knp]}"
    )

    function bind2maps () {
        local i sequence widget
        local -a maps

        while [[ "$1" != "--" ]]; do
            maps+=( "$1" )
            shift
        done
        shift

        sequence="${key[$1]}"
        widget="$2"

        [[ -z "$sequence" ]] && return 1

        for i in "${maps[@]}"; do
            bindkey -M "$i" "$sequence" "$widget"
        done
    }

    bind2maps emacs             -- BackSpace   backward-delete-char
    bind2maps       viins       -- BackSpace   vi-backward-delete-char
    bind2maps             vicmd -- BackSpace   vi-backward-char
    bind2maps emacs             -- Home        beginning-of-line
    bind2maps       viins vicmd -- Home        vi-beginning-of-line
    bind2maps emacs             -- End         end-of-line
    bind2maps       viins vicmd -- End         vi-end-of-line
    bind2maps emacs viins       -- Insert      overwrite-mode
    bind2maps             vicmd -- Insert      vi-insert
    bind2maps emacs             -- Delete      delete-char
    bind2maps       viins vicmd -- Delete      vi-delete-char
    bind2maps emacs viins vicmd -- Up          up-line-or-history
    bind2maps emacs viins vicmd -- Down        down-line-or-history
    bind2maps emacs             -- Left        backward-char
    bind2maps       viins vicmd -- Left        vi-backward-char
    bind2maps emacs             -- Right       forward-char
    bind2maps       viins vicmd -- Right       vi-forward-char

    # Make sure the terminal is in application mode, when zle is
    # active. Only then are the values from $terminfo valid.
    if (( ${+terminfo[smkx]} )) && (( ${+terminfo[rmkx]} )); then
        function zle-line-init () {
            emulate -L zsh
            printf '%s' ${terminfo[smkx]}
        }
        function zle-line-finish () {
            emulate -L zsh
            printf '%s' ${terminfo[rmkx]}
        }
        zle -N zle-line-init
        zle -N zle-line-finish
    else
        for i in {s,r}mkx; do
            (( ${+terminfo[$i]} )) || debian_missing_features+=($i)
        done
        unset i
    fi

    unfunction bind2maps

fi # [[ -z "$DEBIAN_PREVENT_KEYBOARD_CHANGES" ]] && [[ "$TERM" != 'emacs' ]]
[...]

上記のように、キー名を対応するエスケープシーケンスにマップする連想配列を定義します。これらのエスケープシーケンスは、デフォルトでDebianに付属のterminfoデータベースから取得されます(アイテムリストはデフォルトでは制限されていますが)。

ただし、terminfoデータベースは端末から送信されたエスケープシーケンスのみを提供するためキーボード転送モード、zshrcがZLEに起動時にこのモードに入り、終了時にそのままにするように指示することがわかります。

zshrcこれは、インタラクティブzshシェル(シェルが実行される場所)のプロンプト(ZLE内)とプロンプトなしで異なるシーケンスが表示される理由を説明します。

zshのスクリプトとより一般的な非対話型呼び出しはzshrcを読みません。 zshrcはすべてのカスタム、エイリアス、関数を保存する場所なので、スクリプトがそれを読むと確実に壊れます。

ZLEvaredも使用され、zshrcを読み取らないスクリプトから呼び出されると、これは/ etc / zsh / zshrcからオペレーティングシステムが提供するバインディングを取得しないことを意味します。

したがって、これらのバインディングが好きで、スクリプトユーザーが同じオペレーティングシステムを使用しているかどうかに関係なく、ユーザーが使用できるようにするには、スクリプトに直接含める必要があります。

また、ZLEのデフォルト値は、emacsorで始まるかどうかに応じてorモードに設定されます。スクリプトユーザーが一貫したキーバインディングを使用できるようにするには、パターンを強制的に適用する必要があります。デフォルトでは、emacsモードまたはviモードが選択されているか(または使用されている)かに応じて、またはのエイリアスであるキーマップバインディングが設定されます。vi$EDITOR$VISUALvi/vibindkeymainemacsviinsbindkey -ebindkey -v

Debianに加えて、他のエディタの動作を模倣するShiftためAltに//と組み合わせることを含む、ファンクションキーバインディングに関する他の多くのカスタム提案を見つけることができます(そのエディタに一意のシーケンスを送信する端末から)。Ctrl

たとえば、参照してください。zsh zle シフト選択スタックオーバーフロー情報oh-my-zshキーバインディング彼らからインスピレーションを得ることができます。


1このような問題が発生しないように、cshスクリプトに通常読み込みをスキップする#! /bin/csh -fshebangがある方法を確認してください。-f~/.cshrc

おすすめ記事