Is there a way to replicate this: TUI dialog(screenshot not mine of course) on something Shell Script or something like C? This looks better than using dialog package.
-
you might want `whiptail` – KamilCuk Aug 13 '22 at 12:58
-
This is essentially a tool recommendation question (a category which is as a whole off-topic here); every reasonable answer will involve `dialog` or one of its many competitors. – Charles Duffy Aug 13 '22 at 15:50
3 Answers
I don't think that you can get the same TUI as in your screenshot with dialog
but here's an attempt at it; I define the colors with a DIALOGRC
generated on the fly:
#!/bin/bash
# DIALOGRC overrides:
IFS='' read -r -d '' dialogrc <<'EOF'
use_shadow = OFF
use_colors = ON
screen_color = (GREEN,BLACK,ON)
dialog_color = screen_color
border_color = screen_color
border2_color = screen_color
inputbox_color = screen_color
button_active_color = screen_color
button_inactive_color = screen_color
button_key_active_color = screen_color
button_key_inactive_color = screen_color
button_label_active_color = screen_color
button_label_inactive_color = screen_color
EOF
input_passwd=$(
3>&1 >/dev/tty \
DIALOGRC=<(printf '%s' "$dialogrc") \
dialog \
--output-fd 3 \
--no-lines \
--insecure \
--passwordbox $' Please enter the passphrase to\n protect your key' 8 60
) || exit 1
printf '\ninput_password=%q\n' "$input_passwd"
Please notice the redirection of dialog
's stdout to /dev/tty
and how I capture the result though a different file descriptor; the order in 3>&1 >/dev/tty
is important for doing so.

- 13,917
- 1
- 23
- 35
Basically every programming language allows you to make TUI or CLI.
Reproducing what you want to achieve
Probably, the most dumb way to implement that is to use ANSI escape codes and UNICODE characters. You can draw a colored menu and move the cursor to override the output or to hide the useless stuff.
Example using bare bash: link to GitHub Gist - password_input_tui.sh
#!/bin/bash
max_pass_length=40
pass_length=30
i=0
password=
prompt=$' \u2502 Passphrase: '
clear
echo -e "\e[32;40m\e[H"
echo -e " \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 "
echo -e " \u2502 Please enter the passphrase to \u2502 "
echo -e " \u2502 protect your new key \u2502 "
echo -e " \u2502 \u2502 "
echo -e -n " \u2502 Passphrase: "; while [ $i -lt $pass_length ]; do echo -e -n "_"; i=$(expr $i + 1); done; while [ $i -lt $max_pass_length ]; do echo -e -n " "; i=$(expr $i + 1); done; echo -e " \u2502 "
echo -e " \u2502 \u2502 "
echo -e " \u2502 <OK> <Cancel> \u2502 "
echo -e " \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 "
echo -e "\e[5;H"
# Read password
i=0
while IFS= read -p "$prompt" -r -s -n 1 char
do
if [[ $i -eq $(expr $pass_length - 1) ]]
then
password+=$'\0'
break
fi
if [[ $char == $'\0' ]]
then
break
fi
if [[ $char == $'\177' || $char == $'\b' ]]
then
if [ $i -gt 0 ]
then
i=$(expr $i - 1)
prompt=$'\b_\b'
password="${password%?}"
else
prompt=''
fi
else
i=$(expr $i + 1)
prompt='*'
password+="$char"
fi
done
# Select <OK> or <Cancel>
choice=1
echo -e -n "\e[8;14H\e[30;42m<OK>\e[0m\e[10H"
while read -r -s -n 1 ui; do
case "$ui" in
$'\x1b') # Handle ESC sequence.
# Flush read. We account for sequences for Fx keys as
# well. 6 should suffice far more then enough.
read -r -s -n 1 -t 0.1 char
if [[ "$char" == "[" ]]; then
read -r -s -n 1 -t 0.1 char
case "$char" in
#"A") printf "Up\n";;
#"B") printf "Down\n";;
"C") # right
echo -e -n "\e[8;14H\e[32;40m<OK>\e[0m"
echo -e -n "\e[8;48H\e[30;42m<Cancel>\e[0m"
echo -e -n "\e[10H"
choice=0;;
"D") # left
echo -e -n "\e[8;14H\e[30;42m<OK>\e[0m"
echo -e -n "\e[8;48H\e[32;40m<Cancel>\e[0m"
echo -e -n "\e[10H"
choice=1;;
esac
fi
# Flush "stdin" with 0.1 sec timeout.
read -rsn5 -t 0.1
;;
$'\0')
break ;;
# Other one byte (char) cases. Here only quit.
q)
choice=1
break;;
esac
done
# Show result
if [[ choice -eq 1 ]]
then
echo "Password: $password"
else
echo "Operation canceled"
fi
exit 0
Output: bash.gif
References:
But that example has many problems and bits that could be improved (for example this code doesn't validate the password characters, and special characters like CTRL+stuff are considered as 2+ characters) and it's probably much harder than using tools made exactly for this task (e.g. dialog).
Another example (C++) using curses: Link to GitHub Gist - password_input_tui.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curses.h>
#define MAX_PASSWD_LENGTH 32
#define M1Y 7
#define M1X 8
#define M2Y 7
#define M2X 33
void menu(int choice);
int yBottom, xBottom;
int yTop, xTop;
WINDOW* win;
int main(int argc, char** argv)
{
initscr();
noecho();
curs_set(0);
int xMax, yMax, i, choice, exit;
char password[MAX_PASSWD_LENGTH];
char ch;
getmaxyx(stdscr, yMax, xMax);
yBottom = yMax / 1.5, xBottom = xMax / 1.5;
yTop = yMax / 6, xTop = xMax / 6;
win = newwin(yBottom, xBottom, yTop, xTop);
box(win, 0, 0);
mvwprintw(win, 2, 2, "Please enter the passphrase to");
mvwprintw(win, 3, 2, "protect your new key");
mvwprintw(win, 5, 2, "Passphrase: ");
i=0;
while (i < MAX_PASSWD_LENGTH) {
ch = wgetch(win);
if (ch == ' ' || ch == 27 || ch == 127) {
continue;
}
else if (ch == '\b' || ch == 8) {
if (i > 0) {
mvwprintw(win, 5, 14 + i, "\b \b");
--i;
}
else {
continue;
}
}
else if (ch == '\n' || ch == 10 || ch == '\r' || ch == '\t') {
break;
}
else if (ch == 0 || ch == 224) {
ch = wgetch(win);
continue;
}
else {
mvwprintw(win, 5, 14 + i, "*");
password[i++] = ch;
}
}
password[i] = '\0';
wattron(win, A_STANDOUT);
mvwprintw(win, M1Y, M1X, "<OK>");
wattroff(win, A_STANDOUT);
mvwprintw(win, M2Y, M2X, "<Cancel>");
choice = 1;
exit = 0;
while(ch = wgetch(win))
{
switch(ch)
{
case '\033':
{
wgetch(win);
switch(wgetch(win))
{
case 'D':
{
choice = 1;
break;
}
case 'C':
{
choice = 2;
break;
}
}
break;
}
case '\n':
{
exit = 1;
break;
}
default:
break;
}
if(exit)
break;
menu(choice);
}
if(choice == 1)
mvwprintw(win, 10, 10, "Password: %s", password);
else
mvwprintw(win, 10, 10, "Operation canceled");
wgetch(win);
endwin();
return 0;
}
void menu(int choice)
{
switch(choice)
{
case 1:
wattron(win, A_STANDOUT);
mvwprintw(win, M1Y, M1X, "<OK>");
wattroff(win, A_STANDOUT);
mvwprintw(win, M2Y, M2X, "<Cancel>");
break;
case 2:
mvwprintw(win, M1Y, M1X, "<OK>");
wattron(win, A_STANDOUT);
mvwprintw(win, M2Y, M2X, "<Cancel>");
wattroff(win, A_STANDOUT);
break;
default:
wattron(win, A_STANDOUT);
mvwprintw(win, M1Y, M1X, "<OK>");
wattroff(win, A_STANDOUT);
mvwprintw(win, M2Y, M2X, "<Cancel>");
break;
}
}
Output: cpp.gif
To make this work (as per dialog) obviously you have to make sure you have installed curses (Example for Debian: sudo apt-get install libncurses5-dev libncursesw5-dev
, and use -lncurses
flag when you compile it with g++).
Some C Code for Windows
I suggest you take a look at TurboVision, which is a TUI framework released for MS-DOS in 1990. There's a interesting modern port on GitHub, which is cross platform. Here's a bit of what you can do: preview.
Otherwise you could include windows.h
header and use SetConsoleTextAttribute()
to color the output:
#include <stdio.h>
#include <windows.h>
int main(int argc, char** argv)
{
HANDLE hConsole;
int k;
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
// you can loop k higher to see more color choices
for (k = 1; k < 255; k++)
{
// pick the colorattribute k you want
SetConsoleTextAttribute(hConsole, k);
printf("%3d %s\n", k, "I want to be nice today!");
}
return 0;
}
Output: example
Or simply handle the input stream with _getch()
to mask the password with asterisks:
#include <stdio.h>
#define PASSWORD_LENGTH 32
int main(int argc, char** argv) {
int i = 0;
char password[PASSWORD_LENGTH + 1];
int ch;
printf("Enter Password (max length: %d): ", PASSWORD_LENGTH);
while (i < PASSWORD_LENGTH) {
ch = _getch();
if (ch == ' ' || ch == 27) {
continue;
}
else if (ch == '\b') {
if (i > 0) {
printf("\b \b");
--i;
}
else {
continue;
}
}
else if (ch == '\r' || ch == '\t') {
break;
}
else if (ch == 0 || ch == 224) {
ch = _getch();
continue;
}
else {
password[i++] = ch;
printf("*");
}
}
password[i] = '\0';
printf("\nPassword: %s", password);
return 0;
}
Output: example

- 1,195
- 3
- 8
- 29
I had to implement a TUI database interface to build workflow for permission database. Given that TUI interface will not look as good as GUI, dialog provides very reasonable solution, and can be done in 1-2 hours.
For single password field, use password box as below. It produces output similar to your screenshot.
input_pw=$(dialog --strdo--insecure --passwordbox "Enter Password ..." 0 0 )
if [ "$input_pw" = "SeCrEt" ] ; then
do-something
fi
Note that for better looking screen you will most likely want to use the "--form" option, which make it easier to "decorate" the screen, add instructions, control field layout, size of widgets, etc. It took me about 1 day of work to figure it out, and integrate it into my scripts (front end for permission database).

- 13,723
- 1
- 10
- 37