Linuxでデフォルトで使われることが多いインタラクティブシェル「bash」を使う際、当然だがカーソルの移動は欠かすことができない。コマンド入力を間違えたり、過去に入力したコマンドを再利用したりするには、編集したい場所までカーソルを移動させて書き換える必要がある。通常は左右の矢印キーを使って移動すればよいのだが、bashは単語単位でカーソルを移動させたり、行頭行末へカーソルを移動させたりするショートカットを提供している。bashの操作効率を引き上げる方法として、まずはこの操作を覚えてしまおう。

カーソルの移動(デフォルト)

詳しい説明は割愛するが、bashではカーソルの操作などを担当する部分が「GNU Readline Library」というライブラリに依存している。そのため、bashで使えるショートカットキーを調べていくと、最終的にGNU Readline Libraryのショートカットキー設定を説明したドキュメントに行き着く。

そのドキュメントの一次ソースは「The GNU Readline User Interface」辺りになる。同ドキュメントの説明から整理すると、カーソルを移動させるショートカットキーはデフォルトでは次のように設定されている。

キー 操作
「Ctrl」+「B」 カーソルを1文字右へ移動。
「Ctrl」+「F」 カーソルを1文字左へ移動。
「Ctrl」+「B」 カーソルを単語の先頭へ移動、すでに単語の先頭の場合は、前の単語の先頭へ移動。
「Alt」+「F」 カーソルを単語の末尾へ移動、すでに単語の末尾の場合は、次の単語の末尾へ移動。
「Ctrl」+「A」 カーソルを行頭へ移動。
「Ctrl」+「E」 カーソルを行末へ移動。

PCが普及するより前に「ワークステーション」と呼ばれる端末を使っていた世代だと、上記のショートカットキー設定はそれほど不思議なものではないのだが、主にWindows 10だけ使っているような方からすると、かなり使いにくくなじみのない設定なのではないかと思う。

実際にはショートカットキーは上記だけではなく、ほかのキーにも設定されている。そちらの設定も見てみよう。

bindコマンドでショートカットキーを調べる

bashではGNU Readline Libraryを使っているのだが、GNU Readline Libraryの提供するデフォルトの設定だけを使っているわけではない。実際にはほかのショートカットキーも設定されており、現在のユーザーになじみのある設定は最初から行われているのだ。

bashでは「bind」という組み込み関数を使うことで、設定されているショートカットキーを確認することができる。いくつかオプションがあるが、「bind -p」といったように-pを使った場合の出力がわかりやすいのではないだろうか。このままだと少々出力量が多いので「bind -p | grep -v ‘^#’ | lv | sort -k 2 | uniq | grep -v self-insert | column -t」のような感じで出力を整え、どのようなショートカットキーが設定されているのか見てみよう。実行すると次のようになる。

[daichi@virt ~]$ bind -p | grep -v '^#' | lv | sort -k 2 | uniq | grep -v self-insert | column -t
"\e          ":                                      set-mark
"\C-g":      abort
"\C-x\C-g":  abort
"\e\C-g":    abort
"\C-j":      accept-line
"\C-m":      accept-line
"\C-b":      backward-char
"\e[D":      backward-char
"\eOD":      backward-char
"\C-?":      backward-delete-char
"\C-h":      backward-delete-char
"\C-x\C-?":  backward-kill-line
"\e\C-?":    backward-kill-word
"\e\C-h":    backward-kill-word
"\e[1;3D":   backward-word
"\e[1;5D":   backward-word
"\eb":       backward-word
"\e<":       beginning-of-history
"\C-a":      beginning-of-line
"\e[H":      beginning-of-line
"\eOH":      beginning-of-line
"\e[200~":   bracketed-paste-begin
"\C-xe":     call-last-kbd-macro
"\C-]":      character-search
"\e\C-]":    character-search-backward
"\C-l":      clear-screen
"\C-i":      complete
"\e\e":      complete
"\e!":       complete-command
"\e/":       complete-filename
"\e@":       complete-hostname
"\e{":       complete-into-braces
"\e~":       complete-username
"\e$":       complete-variable
"\C-d":      delete-char
"\e[3~":     delete-char
"\e\\":      delete-horizontal-space
"\e-":       digit-argument
"\e0":       digit-argument
"\e1":       digit-argument
"\e2":       digit-argument
"\e3":       digit-argument
"\e4":       digit-argument
"\e5":       digit-argument
"\e6":       digit-argument
"\e7":       digit-argument
"\e8":       digit-argument
"\e9":       digit-argument
"\C-x\C-v":  display-shell-version
"\C-xA":     do-lowercase-version
"\C-xB":     do-lowercase-version
"\C-xC":     do-lowercase-version
"\C-xD":     do-lowercase-version
"\C-xE":     do-lowercase-version
"\C-xF":     do-lowercase-version
"\C-xG":     do-lowercase-version
"\C-xH":     do-lowercase-version
"\C-xI":     do-lowercase-version
"\C-xJ":     do-lowercase-version
"\C-xK":     do-lowercase-version
"\C-xL":     do-lowercase-version
"\C-xM":     do-lowercase-version
"\C-xN":     do-lowercase-version
"\C-xO":     do-lowercase-version
"\C-xP":     do-lowercase-version
"\C-xQ":     do-lowercase-version
"\C-xR":     do-lowercase-version
"\C-xS":     do-lowercase-version
"\C-xT":     do-lowercase-version
"\C-xU":     do-lowercase-version
"\C-xV":     do-lowercase-version
"\C-xW":     do-lowercase-version
"\C-xX":     do-lowercase-version
"\C-xY":     do-lowercase-version
"\C-xZ":     do-lowercase-version
"\eA":       do-lowercase-version
"\eB":       do-lowercase-version
"\eC":       do-lowercase-version
"\eD":       do-lowercase-version
"\eE":       do-lowercase-version
"\eF":       do-lowercase-version
"\eG":       do-lowercase-version
"\eH":       do-lowercase-version
"\eI":       do-lowercase-version
"\eJ":       do-lowercase-version
"\eK":       do-lowercase-version
"\eL":       do-lowercase-version
"\eM":       do-lowercase-version
"\eN":       do-lowercase-version
"\eP":       do-lowercase-version
"\eQ":       do-lowercase-version
"\eR":       do-lowercase-version
"\eS":       do-lowercase-version
"\eT":       do-lowercase-version
"\eU":       do-lowercase-version
"\eV":       do-lowercase-version
"\eW":       do-lowercase-version
"\eX":       do-lowercase-version
"\eY":       do-lowercase-version
"\eZ":       do-lowercase-version
"\el":       downcase-word
"\e\C-i":    dynamic-complete-history
"\C-x\C-e":  edit-and-execute-command
"\C-x)":     end-kbd-macro
"\e>":       end-of-history
"\C-e":      end-of-line
"\e[F":      end-of-line
"\eOF":      end-of-line
"\C-x\C-x":  exchange-point-and-mark
"\C-f":      forward-char
"\e[C":      forward-char
"\eOC":      forward-char
"\C-s":      forward-search-history
"\e[1;3C":   forward-word
"\e[1;5C":   forward-word
"\ef":       forward-word
"\eg":       glob-complete-word
"\C-x*":     glob-expand-word
"\C-xg":     glob-list-expansions
"\e^":       history-expand-line
"\e#":       insert-comment
"\e*":       insert-completions
"\e_":       insert-last-argument
"\e.":       insert-last-argument
"\C-k":      kill-line
"\e[3;5~":   kill-word
"\ed":       kill-word
"\C-n":      next-history
"\e[B":      next-history
"\eOB":      next-history
"\en":       non-incremental-forward-search-history
"\ep":       non-incremental-reverse-search-history
"\C-o":      operate-and-get-next
"\e[2~":     overwrite-mode
"\C-x!":     possible-command-completions
"\e?":       possible-completions
"\e=":       possible-completions
"\C-x/":     possible-filename-completions
"\C-x@":     possible-hostname-completions
"\C-x~":     possible-username-completions
"\C-x$":     possible-variable-completions
"\C-p":      previous-history
"\e[A":      previous-history
"\eOA":      previous-history
"\C-q":      quoted-insert
"\C-v":      quoted-insert
"\C-x\C-r":  re-read-init-file
"\e[0n":     redraw-current-line
"\er":       redraw-current-line
"\e\C-r":    revert-line
"\C-@":      set-mark
"\e\C-e":    shell-expand-line
"\C-x(":     start-kbd-macro
"\e&":       tilde-expand
"\et":       transpose-words
"\C-_":      undo
"\C-x\C-u":  undo
"\C-u":      unix-line-discard
"\C-w":      unix-word-rubout
"\eu":       upcase-word
"\C-z":      vi-editing-mode
"\C-y":      yank
"\e_":       yank-last-arg
"\e.":       yank-last-arg
"\e\C-y":    yank-nth-arg
"\ey":       yank-pop
[daichi@virt ~]$

この出力は全てのプラットフォームで同じではない点には注意が必要だ。同じディストリビューションでもバージョンが異なると出力される内容が若干異なる可能性がある。上記の設定は自分で上書きすることができるので、とりあえずこのように設定されているんだな、ということがわかっていれば現時点では問題ない。

出力の左側が謎めいて見えるかもしれないが、これは「どのキーが押されたか」を表現している。詳細はおいおい説明するとして、今は「この表記がどのキーが押されたのかを示している」ということだけわかっておいてもらえればよい。

設定されているショートカットキーを整理する

先ほどの「bind -p」の出力からカーソルキーの移動に関するショートカットキーの設定を取り出して、最初の表に追加すると次のようになる。

キー 操作
「Ctrl」+「B」 カーソルを1文字右へ移動
「Ctrl」+「F」 カーソルを1文字左へ移動
「→」 カーソルを1文字右へ移動
「←」 カーソルを1文字左へ移動
「Alt」+「F」 カーソルを単語の末尾へ移動、すでに単語の末尾の場合は、次の単語の末尾へ移動
「Alt」+「B」 カーソルを単語の先頭へ移動、すでに単語の先頭の場合は、前の単語の先頭へ移動。
「Alt」+「→」 カーソルを単語の末尾へ移動、すでに単語の末尾の場合は、次の単語の末尾へ移動
「Alt」+「←」 カーソルを単語の先頭へ移動、すでに単語の先頭の場合は、前の単語の先頭へ移動
「Ctrl」+「→」 カーソルを単語の末尾へ移動、すでに単語の末尾の場合は、次の単語の末尾へ移動
「Ctrl」+「←」 カーソルを単語の先頭へ移動、すでに単語の先頭の場合は、前の単語の先頭へ移動
「Ctrl」+「A」 カーソルを行頭へ移動
「Ctrl」+「E」 カーソルを行末へ移動
「Home」 カーソルを行頭へ移動
「End」 カーソルを行末へ移動

だいぶ見慣れたショートカットキーがあることに気がついただろう。bashはさすがに広く使われているインタラクティブシェルだけあって、Windowsで使われているようなショートカットキーは最初からbashでも使用できるように設定されているのだ。

カーソルの移動(Windows向け)

ということで、上記テーブルからさらにWindowsでもよく使われているショートカットキーに絞って表をシェイプアップすると、次のようになる。

キー 操作
「→」 カーソルを1文字右へ移動
「←」 カーソルを1文字左へ移動
「Home」 カーソルを行頭へ移動
「End」 カーソルを行末へ移動
「Ctrl」+「→」 カーソルを単語の末尾へ移動、すでに単語の末尾の場合は、次の単語の末尾へ移動
「Ctrl」+「←」 カーソルを単語の先頭へ移動、すでに単語の先頭の場合は、前の単語の先頭へ移動

上記ショートカットキーはほぼ同じ意味でWindowsのほかのアプリケーションでも使われていることが多い。つまり、このショートカットキーはbashでも使えるし、Windowsでも活用できる。

仕事ではWindows 10を使うことが多い、または、仕事自体はLinuxで行っていても、Linuxにアクセスする端末はWindows 10のノートPCとかデスクトップであるといったことは多いのだ。つまり、Linuxだけで使えるショートカットキーを覚えるよりも、できればWindowsでも使われることが多いショートカットキーを覚えたほうが、仕事の効率は上がりやすいということになる。

bashは多くのショートカットキーを提供している。次回以降、こういった感じでWindowsを使っている場合に特に有利になる設定を取り上げていこうと思う。もしWindows側でこうしたショートカットキーを使ったことがないのであれば、これを機にぜひ使ってみてほしい。いろいろとはかどるはずだ。

参考