私は何よりも選択リストを作成するスクリプトを作業しています。良い:
1)プロジェクト1 #(最も明るい部分) 2)項目2 三)アイテム3#(選択済み) 4)項目4
- ユーザーが次の項目を押すと
down-arrow
強調表示 - ユーザーが前の項目をタップすると
up-arrow
強調表示されます - など。
- ユーザーが
tab
選択した項目を押したとき - ユーザーが押すと、
shift+tab
すべての項目が選択/選択解除されます。 - ユーザーが押すと、
ctrl+a
すべての項目が選択されます - ...
現在の使用ではうまくいきます。これは、入力が自分の設定に基づいてフィルタリングされる個人的なユースケースです。
問題は、これをどのように多様な端末で安定的にするかである。
私は入力を読むためにいくつかのハッキーソリューションを使用します。
while read -rsn1 k # Read one key (first byte in key press)
do
case "$k" in
[[:graph:]])
# Normal input handling
;;
$'\x09') # TAB
# Routine for selecting current item
;;
$'\x7f') # Back-Space
# Routine for back-space
;;
$'\x01') # Ctrl+A
# Routine for ctrl+a
;;
...
$'\x1b') # ESC
read -rsn1 k
[ "$k" == "" ] && return # Esc-Key
[ "$k" == "[" ] && read -rsn1 k
[ "$k" == "O" ] && read -rsn1 k
case "$k" in
A) # Up
# Routine for handling arrow-up-key
;;
B) # Down
# Routine for handling arrow-down-key
;;
...
esac
read -rsn4 -t .1 # Try to flush out other sequences ...
esac
done
など。
前述のように、問題はさまざまな端末でこれを安定させる方法です。つまり、特定のキーを定義するバイトシーケンスが何であるかです。バッシュでこれは可能ですか?
1つのアイデアは、与えられた結果に基づいてtput
orとフィルタリングを使用することです。しかし、実際にキーを押したときに実際に読んだ内容と両方がinfocmp
違うという点で難関にぶつかりました。たとえば、bashの代わりにCを使用している場合も同様です。tput
infocmp
for t in $(find /lib/terminfo -type f -printf "%f\n"); {
printf "%s\n" "$t:";
infocmp -L1 $t | grep -E 'key_(left|right|up|down|home|end)';
}
降伏順序は、例えばで定義されているように読み取られますが、によって設定されたものではlinux
ありません。xterm
TERM
たとえば、左矢印は次のようになります。
tput
/infocmp
:\x1 O D
read
:\x1 [ D
私は何を見逃していますか?
答え1
あなたが見逃しているのは、ほとんどの端末記述(linux
ハードコードされた文字列の一般的な使用のため、ここでは少数のみ.inputrc
)が以下を使用することです。申請方法特殊キーに使用されます。これにより、カーソルキーが示されているように表示されtput
(infocmp
初期化されていない)端末が送信するのとは異なります。呪いアプリケーションは常に端末を初期化し、端末データベースが使用されます。それ目的。
dialog
用途がありますが、問題を直接解決するわけではありません。一方、面倒です(技術的に実現可能、珍しい完璧)はbash専用のソリューションを提供します。通常、私たちはこれに別の言語を使用します。
特殊キーを読み取る際の問題は、通常、escapeなどの厄介な文字を含む複数バイトであることです~。あなたはできますするBashを使用してこれを行うことは可能ですが、これがどの特殊キーであるかをポータブルに決定する問題を解決する必要があります。
dialog
どちらも特殊なキーストロークを処理し(一時的に)ディスプレイに代わるものです。本当に単純なコマンドラインプログラムが必要な場合はこれはありませんdialog
。
以下は、特殊キーを読み取り、それを印刷する簡単なCプログラムです。印刷可能(そして移植可能な)フォーマット:
#include <curses.h>
int
main(void)
{
int ch;
const char *result;
char buffer[80];
filter();
newterm(NULL, stderr, stdin);
keypad(stdscr, TRUE);
noecho();
cbreak();
ch = getch();
if ((result = keyname(ch)) == 0) {
/* ncurses does the whole thing, other implementations need this */
if ((result = unctrl((chtype)ch)) == 0) {
sprintf(buffer, "%#x", ch);
result = buffer;
}
}
endwin();
printf("%s\n", result);
return 0;
}
これがあると仮定すると、tgetch
スクリプトで次のように使用できます。
case $(tgetch 2>/dev/null) in
KEY_UP)
echo "got cursor-up"
;;
KEY_BACKSPACE|"^H")
echo "got backspace"
;;
esac
追加資料:
- カーソルキーが機能しない(ncurses FAQ)
dialog
- スクリプトベースの呪いウィジェット(アプリケーションとライブラリ)- キー名/キー名
答え2
を使ってみましたdialog
か?これはほとんどのLinuxディストリビューションでは標準であり、チェックリストを含むさまざまなテキストベースのダイアログボックスを作成できます。
たとえば、
exec 3>&1 # open temporary file handle and redirect it to stdout
# type title width height n-items
items=$(dialog --no-lines --checklist "Title here" 20 70 4 \
1 "Item 1" on \
2 "Item 2" off \
3 "Item 3" on \
4 "Item 4" off \
2>&1 1>&3) # redirect stderr to stdout to catch output,
# redirect stdout to temporary file
selected_OK=$? # store result value
exec 3>&- # close new file handle
# handle output
if [ $selected_OK = 0 ]; then
echo "OK was selected."
for item in $items; do
echo "Item $item was selected."
done
else
echo "Cancel was selected."
fi
あなたは次のようなものを得るでしょう:
出力は次のとおりです。
OK was selected.
Item 1 was selected.
Item 3 was selected.
(または希望のプロジェクト)。
man dialog
作成できるさまざまな種類のダイアログボックスと、外観をカスタマイズする方法に関する情報が提供されます。