シェルスクリプトで動作するコマンドラインインタフェース
シェルスクリプトで動作するコマンドラインインタフェースを作りました。
1.はじめに
Linuxにログインしたターミナル上では、標準入力した文字列(コマンドライン)を編集することができます。また矢印キーでコマンド履歴を表示・編集することもできます。
が、シェルスクリプトを起動したあとの標準入力を、同じように編集したり、コマンド履歴を表示することはできません。
ネットを探しましたが同じようなものがみつかりませんでした。
ということで、シェルスクリプト上でコマンドラインを編集できるスクリプトを作ってみました。
このスクリプトでできるコマンドラインインタフェースは下記です。
- Ctrl+a:カーソルを先頭に移動
- Ctrl+b:カーソルを1文字左に移動
- Ctrl+d:カーソル上の文字を削除
- Ctrl+e:カーソルを末尾に移動
- Ctrl+f:カーソルを1文字右に移動
- Ctrl+k:カーソルより後方の文字を削除
- Ctrl+u:カーソルより前方の文字を削除
- Backspace:カーソルの左側の文字を削除
- 上矢印:ヒストリ呼び出し
- 下矢印:ヒストリ呼び出し
- 左矢印:カーソルを1文字左に移動
- 右矢印:カーソルを1文字右に移動
- リターン:コマンド実行
スクリプトを任意の名称で保存して実行すると、スクリプトで定義したプロンプトを表示して標準入力待ちになります。
ご自身のスクリプトに合うよう、適当に組み込んでください。
不具合・改善等があれば適宜アップデートしていきます。
CentOS 7で動作確認しています。環境によっては期待通り動作しない可能性があります。予めご了承ください。
2.スクリプト
#!/bin/bash
# Copyright (c) 2020 www.koikikukan.com
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom
# the Software is furnished to do so, subject to the following
# conditions:
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
history -r /root/.bash_history
# プロンプト
PROMPT="# "
PROMPT_LENGTH=${#PROMPT}
CURSOL_INIT_POS=$((PROMPT_LENGTH + 1))
COMMAND=''
LENGTH=0
COUNTER=0
POS=${CURSOL_INIT_POS}
function _history_update() {
tput cub 1000
tput dl 1
COMMAND=`history | tail -$COUNTER | head -1 | sed -re "s/^\s+[0-9]+\s{2}//"`
echo -n "${PROMPT}$COMMAND"
LENGTH=${#COMMAND}
POS=$((LENGTH + CURSOL_INIT_POS))
}
echo -n "${PROMPT}"
while IFS= read -r -n1 -s char; do
case $char in
$'\x1b')
read -r -n2 -s rest
char+="$rest"
case $char in
$'\033\x5b\x41')
HISTORY_COUNT=`history|wc -l`
if [ $COUNTER -le $HISTORY_COUNT ]; then
COUNTER=$((COUNTER + 1))
_history_update
fi
;;
$'\033\x5b\x42')
if [ $COUNTER -gt 0 ]; then
COUNTER=$((COUNTER - 1))
_history_update
fi
;;
$'\033\x5b\x43')
COMMAND_POS=$((LENGTH + PROMPT_LENGTH))
if [ ${COMMAND_POS} -ge ${POS} ]; then
tput cuf 1
POS=$((POS + 1))
fi
;;
$'\033\x5b\x44')
if [ ${POS} -gt ${CURSOL_INIT_POS} ]; then
tput cub 1
POS=$((POS - 1))
fi
;;
esac
;;
$'\x08')
if [ ${POS} -gt ${CURSOL_INIT_POS} ]; then
LEFT_LENGTH=$((POS - CURSOL_INIT_POS))
if [ ${LENGTH} -gt ${LEFT_LENGTH} ]; then
TMP=$((LEFT_LENGTH - 1))
COMMAND=`echo "${COMMAND}" | sed -e "s/^\(.\{${TMP}\}\).\(.*\)$/\1\2/"`
else
DEL=${COMMAND: -1}
COMMAND="${COMMAND/%${DEL}/}"
fi
tput dl 1
tput cub 1000
echo -n "${PROMPT}${COMMAND}"
if [ ${LENGTH} -gt ${LEFT_LENGTH} ]; then
tput cub 1000
POS2=$((POS - 2))
tput cuf ${POS2}
fi
POS=$((POS - 1))
LENGTH=$((LENGTH - 1))
fi
;;
$'\0a')
history -s "${COMMAND}"
echo
if [ -n "$COMMAND" ];then
eval "${COMMAND}"
fi
LENGTH=0
COMMAND=''
POS=$CURSOL_INIT_POS
echo -n "${PROMPT}"
;;
$'\ca')
if [ ${POS} -gt ${CURSOL_INIT_POS} ]; then
tput cub 1000
tput cuf ${PROMPT_LENGTH}
POS=$CURSOL_INIT_POS
fi
;;
$'\cb')
if [ ${POS} -gt ${CURSOL_INIT_POS} ]; then
tput cub 1
POS=$((POS - 1))
fi
;;
$'\cd')
TMP=$((LENGTH + PROMPT_LENGTH))
if [ ${POS} -le ${TMP} ]; then
LEFT_LENGTH=$((POS - CURSOL_INIT_POS))
if [ ${POS} -ne ${CURSOL_INIT_POS} ]; then
COMMAND=`echo "${COMMAND}" | sed -e "s/^\(.\{${LEFT_LENGTH}\}\).\(.*\)$/\1\2/"`
else
COMMAND="${COMMAND:1}"
fi
tput dl 1
tput cub 1000
echo -n "${PROMPT}${COMMAND}"
if [ ${LENGTH} -gt ${LEFT_LENGTH} ]; then
tput cub 1000
POS2=$((POS - 1))
tput cuf ${POS2}
fi
LENGTH=$((LENGTH - 1))
fi
;;
$'\ce')
COMMAND_POS=$((LENGTH + PROMPT_LENGTH))
if [ ${COMMAND_POS} -ge ${POS} ]; then
tput cub 1000
tput cuf $COMMAND_POS
POS=$((COMMAND_POS+1))
fi
;;
$'\cf')
COMMAND_POS=$((LENGTH + PROMPT_LENGTH))
if [ ${COMMAND_POS} -ge ${POS} ]; then
tput cuf 1
POS=$((POS + 1))
fi
;;
$'\ck')
LEFT_LENGTH=$((POS - CURSOL_INIT_POS))
if [ ${LENGTH} -gt ${LEFT_LENGTH} ]; then
COMMAND=`echo "${COMMAND}" | sed -e "s/^\(.\{${LEFT_LENGTH}\}\).*$/\1/"`
else
continue
fi
tput dl 1
tput cub 1000
echo -n "${PROMPT}${COMMAND}"
LENGTH=$((POS - CURSOL_INIT_POS))
;;
$'\cu')
LEFT_LENGTH=$((POS - CURSOL_INIT_POS))
RIGHT_LENGTH=$((LENGTH - LEFT_LENGTH))
if [ ${POS} -gt ${CURSOL_INIT_POS} ]; then
COMMAND=`echo "${COMMAND}" | sed -e "s/^.*\(.\{${RIGHT_LENGTH}\}\)$/\1/"`
else
continue
fi
tput dl 1
tput cub 1000
echo -n "${PROMPT}${COMMAND}"
tput cub 1000
tput cuf $PROMPT_LENGTH
POS=$CURSOL_INIT_POS
LENGTH=$((RIGHT_LENGTH))
;;
*)
LEFT_LENGTH=$((POS - CURSOL_INIT_POS))
if [ ${LENGTH} -gt ${LEFT_LENGTH} ]; then
COMMAND=`echo "${COMMAND}" | sed -e "s/^\(.\{${LEFT_LENGTH}\}\)/\1$char/"`
else
COMMAND="${COMMAND}$char"
fi
tput dl 1
tput cub 1000
echo -n "${PROMPT}${COMMAND}"
if [ ${LENGTH} -gt ${LEFT_LENGTH} ]; then
tput cub 1000
tput cuf ${POS}
fi
POS=$((POS + 1))
LENGTH=$((LENGTH + 1))
;;
esac
done
3.ライセンス
本スクリプトはMITライセンスです。
Posted by yujiro このページの先頭に戻る
- bashで小数点を比較する方法
- bashの配列をjoinする方法
- bashのif文で正規表現を使用する方法
- Bashで変数を大文字または小文字に変換する方法
- bashの変数をsplitして配列を作る方法
- シェルスクリプトでtelnet接続する方法
- bashで小数点を比較する方法
- bashでCGIを実行する
- bashで「value too great for base」というエラーになる場合の対処
- bashのfor文のまとめ
- bashで演算を行う方法
- bashで引数に配列を設定する方法
- bashで変数の値を変数名にする方法
- bashで変数を正規表現で置換して変数に代入する方法
- bashで変数を簡単に制御するための演算子
zenback
人気エントリー
トラックバックURL
コメントする
greeting