Bash script programming - bash

I want to keep printing in a while loop(this occurs every second , i.e. sleeps for one second) and keep asking for a user input in a separate while loop, however if one indefinite runs , it doesn't go to the other for loop and if we run one in background and one in foreground still it doesnt helps, I am programming in bash script
#!/bin/bash
memusageStatus=false
diskspace=false
processStatus=false
menuStatus=true
function printMenu {
while [ true ]
do
if $menuStatus ; then
printf "\na) Show/Hide Memory Usage Information \nb) Show/Hide Disk Space Information \nc) Show/Hide Process Information\no) Show/Hide List of options \nq) Exit\n"
fi
sleep 1
tput cup 0 0 ;
tput ed
done
}
function disMenu {
while [ true ]
do
read ab
echo ab
case $ab in
'a') if $memusageStatus ; then
# free -k
memusageStatus=false;
else
memusageStatus=true;
fi
;;
'b') if $diskspace ; then
diskspace=false
else
diskspace=true;
fi
# df -h
;;
'c') if $processStatus ; then
processStatus=false
else
processStatus=true;
fi
;; #ps u
'o') echo "you pressed o"
if $menuStatus ; then
menuStatus=false;
else
menuStatus=true;
fi ;;
'q') exit 0;;
esac
done
}
printMenu &
disMenu
exit 0

You can try to use a read -t 1 in only one loop instead of a sleep 1.
But maybe with a longer read time.
Something like:
while [ true ]
do
echo "Show your Menu"
read -t 5 ab
case $ab in
'q') exit;;
esac
done

Related

Bash Case/Switch Formatting

I'm trying to write a case/switch statement in my bash script as follows:
case "$REPLY" in
E*|e*) $EDITOR "$COMMIT_MSG_FILE" < $TTY; continue ;;
Y*|y*) exit 0 ;;
N*|n*) exit 1 ;;
*) SKIP_DISPLAY_WARNINGS=1; create_prompt; continue ;;
esac
However, I keep getting
syntax error near unexpected token ';;'
E*|e*) $EDITOR "$COMMIT_MSG_FILE" < $TTY; continue ;;'
From reading around, I know that ;; is the equivalent of a break statement in a traditional switch statement, but I'm not sure why I'm getting a syntax error here. All of the functions and variables are defined above, so I can't see that being an issue. Any advice?
EDIT: Entirety of the loop:
while true; do
read_commit_message
check_commit_valid
# if there are no warnings, then the commit is good and we can exit!
test ${#WARNINGS[#]} -eq 0 && exit 0;
# if we're still here, there are warnings we need to display
show_warnings
# if non-interactive don't prompt and exit with an error
# need interactivity for the prompt to show and get response
if [ ! -t 1 ] && [ -z ${FAKE_TTY+x} ]; then
exit 1
fi
# show message asking for proceed, etc
echo -en "${BLUE}Proceed with commit? [e/y/n/?] ${NO_COLOR}"
# read the response
read REPLY < "$TTY"
# Check if the reply is valid
case "$REPLY" in
E*|e*) $EDITOR "$COMMIT_MSG_FILE" < $TTY; continue ;;
Y*|y*) exit 0 ;;
N*|n*) exit 1 ;;
*) SKIP_DISPLAY_WARNINGS=1; create_prompt; continue ;;
esac
done

How can I change output from a Bash script while waiting on user input?

I want to build a stopwatch in Bash, with a pause feature. It should display an incrementing counter, like this one does, but pause it when I hit the "p" key.
How should I implement that? If I wait for user input with read I can't refresh the counter on the screen at the same time. Putting the read inside a loop, with a timeout, is my best plan so far, but it's non-trivial to use a timeout less than one second, which is what I would need here. (It's not supported by read or GNU timeout.) Interrupts would work, but I'd like to support arbitrary keys like "p" and "x".
Is there a reasonably simple way to achieve this?
Print to console while waiting for user input
Write one function that creates the output (example with: counter or if you like spin).
Write one function to read in user commands (readCommand)
Call both functions in a loop
Set timeouts so, that key presses are read soon enough. (sleep .1 and read -t.1)
function readCommand(){
lastCommand=$1
read -t.1 -n1 c;
if [ "$c" = "p" ]
then
printf "\n\r";
return 0
fi
if [ "$c" = "g" ]
then
printf "\n\r";
return 1
fi
return $lastCommand
}
function spin(){
for i in / - \\ \| ;
do
printf "\r$i";
sleep .1;
done
}
function countUp(){
currentCount=$1
return `expr $currentCount + 1`
}
function counter(){
countUp $count
count=$?
printf "\r$count"
sleep .1;
}
command=1
count=0
while :
do
if [[ $command == 1 ]]
then
counter
fi
readCommand $command
command=$?
done
The counter will stop if user presses 'p' and go on if user presses 'g'
Simple script with file descriptor and simple input redirection, leaving no temporary files to cleanup. The waiting is done by using read parameter -t.
counter() {
while ! read -t 0.05 -n1 _; do
printf '\r\t%s' "$(date +%T.%N)"
done
}
{
IFS= read -p "Your name, Sir?"$'\n' -r name
echo >&3
} 3> >(counter "$tmp")
echo "Sir $name, we exit"
Example output:
Your name, Sir?
2:12:17.153951623l
Sir Kamil, we exit
I have made a change in the code you refer.
...
while [ true ]; do
if [ -z $(cat /tmp/pause) ]; then
STOPWATCH=$(TZ=UTC datef $DATE_INPUT $DATE_FORMAT | ( [[ "$NANOS_SUPPORTED" ]] && sed 's/.\{7\}$//' || cat ) )
printf "\r\e%s" $STOPWATCH
sleep 0.03
fi
done
So what you need to do now is a shell script that waits the "p" char from stdin and writes 1 > /tmp/pause or clean /tmp/pause to get he stopwatch paused or working.
something like:
while read char;
do
if [ $char == "p" ]; then
if [ -z $(cat /tmp/pause) ];then
echo 1 > /tmp/pause
else
echo > /tmp/pause
fi
char=0
fi
done < /dev/stdin

Bash script while condition fail to work

I'm new to bash script. I wrote this small script today. When I run it, it returns error: ./test1.sh: line 8: [n != y]]: command not found. I've tried several different combinations and can't get it to work.
#!/bin/bash
function ask_yes_or_no
{
local exitLoop="n"
local answer=""
while ["$exitLoop" != "y"]]
do
read -p "$1 (y/n)? " choice
case "$choice" in
y|Y )
answer="y"
exitLoop="y"
;;
n|N )
answer="y"
exitLoop="y"
;;
esac
done
echo $answer
}
retVal=$(ask_yes_or_no "Do you want to continue")
echo $retVal;
exit 0
If you put all together the remarks in my comment, and with help of shellcheck online, there is below a first version:
#!/bin/bash --
function ask_yes_or_no()
{
local exitLoop="n"
local answer=""
while [ "$exitLoop" != "y" ]
do
read -r -p "$1 (y/n)? " choice
case "$choice" in
y|Y )
answer="y"
exitLoop="y"
;;
n|N )
answer="n"
exitLoop="y"
;;
esac
done
echo $answer
}
retVal=$(ask_yes_or_no "Do you want to continue")
echo "$retVal";
exit 0
So, your very first try is almost good. As written in the comment, there was 2 ending ]] instead of ] in the while condition, and there are important space chars to add. The rest is from shellcheck (which can be installed as a command line).
The test is below:
chmod +x ./myscript.sh
./myscript.sh
Do you want to continue (y/n)? y
y
./myscript.sh
Do you want to continue (y/n)? n
n

How to display the key pressed by the user?

please help how to add variables in this script, does anyone know? Thank you all.
#!/bin/bash
ARROW_UP=??? # I do not know
ARROW_DOWN=??? # I do not know
ARROW_LEFT=??? # I do not know
ARROW_RIGHT=??? # I do not know
ARROW_ENTER=??? # I do not know
case "$KEY" in "$ARROW_UP") echo "press the up arrow key"
"$ARROW_DOWN") echo "press the down arrow key";;
"$ARROW_LEFT") echo "press the left arrow key"
"$ARROW_RIGHT") echo "press the right arrow key"
"$ARROW_ENTER") echo "press the enter key"
esac
Try something like this (you might want to add a case to break out of the loop):
#!/bin/bash
# Reset terminal to current state when we exit.
trap "stty $(stty -g)" EXIT
# Disable echo and special characters, set input timeout to 0.2 seconds.
stty -echo -icanon time 2 || exit $?
# String containing all keypresses.
KEYS=""
# Set field separator to BEL (should not occur in keypresses)
IFS=$'\a'
# Input loop.
while [ 1 ]; do
# Read more input from keyboard when necessary.
while read -t 0 ; do
read -s -r -d "" -N 1 -t 0.2 CHAR && KEYS="$KEYS$CHAR" || break
done
# If no keys to process, wait 0.05 seconds and retry.
if [ -z "$KEYS" ]; then
sleep 0.05
continue
fi
# Check the first (next) keypress in the buffer.
case "$KEYS" in
$'\x1B\x5B\x41'*) # Up
KEYS="${KEYS##???}"
echo "Up"
;;
$'\x1B\x5B\x42'*) # Down
KEYS="${KEYS##???}"
echo "Down"
;;
$'\x1B\x5B\x44'*) # Left
KEYS="${KEYS##???}"
echo "Left"
;;
$'\x1B\x5B\x43'*) # Right
KEYS="${KEYS##???}"
echo "Right"
;;
esac
done
More details here.
The short answer is you can't. Use a real programming language.
Some overly complex solutions can be found here, but I don't endorse them. The KEYBD trap solution at the bottom of the same page is good, but requires ksh93.

handle user input on background

I want to handle user input, but in the background, like in a new thread.
For example, show a progress bar, and when the user hits R, the progress bar resets, or if the user hits Q, the script exits.
I don't want the script to wait for user input. Just render everything and if the user hits any key do something.
Is it posible in bash?
Thanks in advance.
EDIT: I need the script ALWAYS read user input but do not interrupt the execution of main loop.Complicated I make myself understood in English
_handle_keys()
{
read -sn1 a
test "$a" == `echo -en "\e"` || continue
read -sn1 a
test "$a" == "[" || break
read -sn1 a
case "$a" in
C) # Derecha
if [ $PALETTE_X -lt $(($COLUMNS-$PALETTE_SIZE)) ] ; then
PALETTE_X=$(($PALETTE_X+1))
fi
;;
D) # Izquierda
if [ $PALETTE_X -gt 0 ] ; then
PALETTE_X=$(($PALETTE_X-1))
fi
;;
esac
}
render()
{
clear
printf "\033[2;0f BALL (X:${BALL_X} | Y:${BALL_Y})"
_palette_render # Actualiza la paleta
_ball_render
}
while true
do
LINES=`tput lines`
COLUMNS=`tput cols`
render
_handle_keys
done
In my script, the ball moves (render>_ball_render) only when a key is pressed because _handle_keys wait for user input.
I made a ugly solution with read -t0.1 but don't like this
PD: Sorry for my last comment, the time edit finish in the middle of my editing
Here is a technique that seems to work. I am basing this on Sam Hocevar's answer to Bash: How to end infinite loop with any key pressed?.
#!/bin/bash
if [ ! -t 0 ]; then
echo "This script must be run from a terminal"
exit 1
fi
stty -echo -icanon time 0 min 0
count=0
keypress=''
while true; do
let count+=1
echo -ne $count'\r'
# This stuff goes in _handle_keys
read keypress
case $keypress in
# This case is for no keypress
"")
;;
$'\e[C')
echo "derecha"
;;
$'\e[D')
echo "izquierda"
;;
# If you want to do something for unknown keys, otherwise leave this out
*)
echo "unknown input $keypress"
;;
esac
# End _handle_keys
done
stty sane
If the stty sane is missed (e.g. because the script gets killed with Ctrl-C), the terminal will be left in a weird state. You may want to look at the trap statement to address this.
You might also add "reset" to the end of the script to reset the terminal into original state, or it might look locked. It will clear the screen as well, so one might want to add a pause before executing the command.

Resources