Readline
CLI (command line interface) 를 사용하는 프로그램에서 line editing, 명령 history, 자동완성 기능을 제공하는 라이브러리로 shell 프롬프트 상에서 명령문을 작성할때 사용하는 키조합이나, 명령 history, 자동완성 기능은 이 라이브러리를 이용하는 것입니다. line editing 에는 emacs 와 vi 두 종류의 키조합을 제공하는데 기본설정은 emacs 이며 set -o emacs
or set -o vi
옵션을 통해 변경할 수 있습니다.
Readline init file
readline 은 ~/.inputrc
설정 파일을 통해 옵션을 설정하고 키조합을 커스터마이징 할 수 있습니다. 다음은 completion-ignore-case
옵션을 설정하여 tab
및 alt-/
키를 이용한 자동완성 시에 대, 소문자 구분없이 하고 alt-space
에는 "git " 을 ctrl-space
에는 "docker " 스트링이 프롬프트 상에 입력되게 하며 펑션키 F9
, F10
에는 자주 사용하는 명령을 바인딩하는 예입니다.
키조합에 사용되는 키값은 read
명령으로 구할 수 있습니다.
구한 키값에서 ^[
문자를 \e
로 수정하여 사용합니다.
$ read # enter 후에 원하는 키를 입력
^[[20~ # F9 키를 누른 상태
# 키값은 터미널별로 다른값이 나올 수 있습니다.
inputrc
파일을 수정한 후에 Ctrl-x Ctrl-r
를 입력하거나 새로 터미널 창을 열면 설정된 기능을 사용할 수 있습니다. 한가지 유의할 점은 window manager 나 터미널창 자체에서 이미 키조합이 설정돼있으면 적용이 되지 않으므로 필요없는 키조합은 먼저 삭제하시기 바랍니다.
~/.inputrc 활용 예제
# 대,소문자 구분없이 word completion
set completion-ignore-case on
# 모드 설정을 하지 않을경우 default 는 emacs 입니다.
# emacs 모드로 설정
# set editing-mode emacs
# vi 모드로 설정
# set editing-mode vi
# emacs 모드일경우 적용
# $if mode=emacs
# alt-space 치면 프롬프트에 git 입력
"\e ": "git "
# ctrl-space 치면 프롬프트에 docker 입력
"\C-@": "docker "
# 이 함수는 원래 \C-x\C-e 키에 바인딩 되어있는데 한 번에 실행하기 위해
# F2 키에 바인딩 합니다. 현재 명령 행상에서 작성 중인 내용과 함께 vi 에디터가 열립니다.
"\eOQ": edit-and-execute-command
# \C-k\C-u 는 각각 kill-line, unix-line-discard 함수로 바인딩되어 있으므로
# 현재 프롬프트에 입력돼있는 스트링을 모두 삭제합니다. \C-m 은 enter 를 의미합니다.
# F11 키에 git pull 명령 바인딩
"\e[23~": "\C-k\C-ugit pull\C-m"
"\e[23;2~": "\C-k\C-ugit diff master@{1} master\C-m"
# F12 키에 git push 명령 바인딩
"\e[24~": "\C-k\C-ugit push\C-m"
# 기존 키바인딩을 undefined 하고 싶으면
# "키값": nop
# $endif
현재 바인딩 되어있는 사용자 정의 키조합은
bind -s
명령으로 조회해 볼 수 있습니다.bind -v
로는 현재 설정되어 있는 readline 옵션을 조회해 볼 수 있습니다.
유용한 키보드 shortcut
이전 명령에서 사용된 마지막 인수를 입력하고 싶을 때는
Alt-.
을 이용하면 됩니다. 연속해서 누르면 이전 명령으로 이동합니다.Ctrl-r
은 명령 history 를 검색하여 입력한 패턴에 해당하는 명령을 불러와 실행할 수 있게 해줍니다. 연속해서 누르면 매칭되는 다음 항목으로 넘어갑니다.프롬프트에서 명령문을 작성중에 editor 를 불러오고 싶을때
Ctrl-x Ctrl-e
를 하면 현재 입력중인 내용과 함께 vi editor 가 열립니다.:wq
명령으로 vi 를 종료하면 수정된 명령이 실행되고:cq
명령으로 종료하면 실행되지 않습니다.tab 키로 파일이름 자동완성이 되지 않을 경우
Alt-/
로 자동완성을 할 수 있습니다.그 외 여러 키조합은 http://readline.kablamo.org/emacs.html 에서 볼 수 있습니다. 이전에 실행된 키조합을 undo (
Ctrl-/
) 할수도 있고 cut & paste 도 할 수 있습니다.여러 단계의 키조합이 설정되어 사용될 경우 키조합 입력 중간에 취소하는 방법은
Ctrl-g
입니다.
현재 바인딩 되어있는 readline 함수 키조합은
bind -p
명령으로 조회해 볼 수 있습니다.bind -l
명령으로 사용 가능한 readline 함수 목록을 볼 수 있습니다.
Quiz
앞서 이전 명령에서 사용된 마지막 인수는 Alt-.
키조합을 이용하여 입력할 수 있다고 하였는데요
그럼 마지막 인수가 아닌 경우는 어떻게 입력할까요?
똑같이
Alt-.
키조합을 이용하지만 그전에 인수의 위치를Alt-숫자
로 먼저 지정을 합니다.
예를 들면 이전 명령에서 사용된 2 번째 인수를 입력하고 싶다면Alt-2 Alt-.
을 입력하면 됩니다.
cut & paste, copy & paste
이 기능은 kill ring 과 Ctrl-y (yank) 함수를 이용합니다. 이름이 좀 생소한데 예전에는 cut & paste 라고 하지 않고 kill, yank 라고 했다고 합니다. Ctrl-u, Ctrl-k, Ctrl-w, Alt-backspace 같은 키조합들은 작성한 명령문의 일부분을 삭제하는데 이때 삭제된 내용이 kill ring 에 들어갑니다. 이후에 Ctrl-y 를 이용하여 paste 할 수 있습니다. 여기서 kill ring 이라고 하는 이유는 paste 할때 Alt-y 로 이전 값을 불러올 수가 있기 때문입니다.
다음은 Ctrl-w (공백을 기준으로 이전 스트링 삭제) 와 Alt-backspace (단어를 기준으로 이전 스트링 삭제) 를 이용해 cut & paste 를 하는 예입니다. cut 한 다음에 바로 paste 하면 copy & paste 를 할 수 있습니다.
Ctrl-w 와 Alt-backspace 는 공백과 단어를 기준으로 이전 스트링을 삭제하기 때문에 종종 kill 하고자 하는 경계가 맞지 않는 경우가 있습니다. 이때는 mark 와 kill 을 이용하여 직접 region 을 지정할 수 있습니다. 이 기능을 이용하기 위해서는 먼저 ~/.inputrc 에 다음과 같은 설정을 합니다.
# ~/.inputrc 에 추가할 내용
# Alt-m : 현재 커서위치에 mark 설정
"\em": set-mark
# Alt-k : mark 한 위치부터 현재 커서까지 내용을 kill ring 에 copy.
"\ek": copy-region-as-kill
# Alt-Shift-k : mark 한 위치부터 현재 커서까지 내용을 copy 함과 동시에 삭제.
"\eK": kill-region
bind toggling
다음은 bind 명령을 이용한 toggling 입니다.
Ctrl-space 에 docker 가 바인딩 되어 있는데 F2 키를 이용해서 vagrant 로 토글하는 기능입니다.
# bind -x 명령은 .inputrc 에서 설정할 수 없으므로 다음 내용을 .bashrc 에 추가합니다.
# bind_toggle.sh 파일을 $PATH 에서 찾을 수 없을 경우 전체 경로를 입력합니다.
# F1 키를 toggle 키로 설정합니다.
bind -x '"\eOP": "source bind_toggle.sh"'
---------------------------------------------------------
# ~/bin/bind_toggle.sh 파일의 내용
# source 명령으로 읽어 들이므로 shebang 라인과 실행 권한은 필요 없습니다.
case $(bind -S | grep -P -m1 -o "(?<=C-@ outputs )\S+") in
git)
bind '"\C-@": "docker "'
echo docker
;;
docker)
bind '"\C-@": "vagrant "'
echo vagrant
;;
vagrant)
bind '"\C-@": "git "'
echo git
;;
esac
토글 단어를 여러개 사용하면 현재 설정을 알기 어려우므로 프롬프트에 표시해 주면 좋습니다.
~/.bashrc 파일의 PS1
프롬프트 설정하는 곳을 찾아서 적당한 위치를 정하여
+=
연산자를 이용해 다음과 같이 추가해 줍니다.
설정된 값은 토글키를 누른 후 다음번 프롬프트가 표시될때 변경 됩니다.
# ~/.bashrc 파일에서 PS1 프롬프트 설정
PS1+='$(
case $(bind -S | grep -P -m1 -o "(?<=C-@ outputs )\S+") in
git) echo G ;;
docker) echo D ;;
vagrant) echo V ;;
esac
)'
Command line history expansion
현재 작성중인 명령행상에서 앞서 타입한 인수를 다시 입력하고 싶을때가 있습니다.
readline 함수인 history-expand-line 과 !#:$
!#:0
!#:1
... command history 기능을 이용해
Alt-h 숫자
키조합에 바인딩 해보겠습니다.
# ~/.inputrc 에 추가할 내용
# "Alt-," 에는 !#:$ 을 바인딩 하고
# "Alt-h 1" 에는 !#:1 을 "Alt-h 2" 에는 !#:2 ... 을 각각 바인딩 합니다.
# "\e^" 는 history-expand-line 함수입니다. 앞서 입력한 !#:1 부분이 확장됩니다.
"\eh1": "!#:1\e^"
"\eh2": "!#:2\e^"
"\eh3": "!#:3\e^"
"\eh4": "!#:4\e^"
"\eh5": "!#:5\e^"
"\eh6": "!#:6\e^"
"\eh7": "!#:7\e^"
"\eh8": "!#:8\e^"
"\eh9": "!#:9\e^"
"\eh0": "!#:0\e^"
"\e,": "!#:$\e^"
이전 명령의 출력 결과를 사용
명령문을 작성할 때 이전 명령문이 아니라 명령의 출력 결과를 사용하고 싶을 때가 있습니다.
.inputrc 에 다음과 같은 설정을 통해 이전 명령의 출력을 입력할 수 있습니다.
# "\e[19~" : F8 키로 설정합니다. "\e[19;2~" 는 SHIFT-F8 입니다.
# \C-u : 현재까지 타입한 명령을 삭제함과 동시에 copy 합니다.
# $(!!)\e\C-e : \e\C-e 는 shell-expand-line 함수로 앞서 입력한 $(!!) 부분이 확장됩니다.
# \C-a : 커서를 라인 처음으로 옮깁니다.
# \C-y : yank 함수로 paste 에 해당합니다. 앞서 \C-u 로 copy 했던 부분을 붙여 넣습니다.
# \C-e : 커서를 라인 끝으로 옮깁니다.
"\e[19~": "\C-u$(!! | awk 'END{print $NF}')\e\C-e\C-a\C-y\C-e"
"\e[19;2~": "\C-u$(!!)\e\C-e\C-a\C-y\C-e"