How to close zenity window by press on Cancel button? - linux

I have until loop and I can't get it how to break the loop when I click Cancel button? My until loop looks like this:
until [[ "$VAR" == "End" && **<second cond. for cancel>** ]]; do
...
The problem is if I want to close the window I have to click on the red cross. If I want to do this with the "cancel" button, the window does not respond to it.
Tried to find the return value information after clicking cancel, and do an expression for that.
I wonder what the condition should be for it to work properly instead <second cond. for cancel>
SOLUTION: as someone said, <secound cond. for cancel> should be $? == 1. It works fine now.

Zenity exits with an exitcode of 1 if you press the cancel button (or close the dialog window, or press <Escape>). You could write something like this:
#!/bin/bash
rc=0
until [[ $VAR == "End" || $rc == 1 ]]; do
VAR=$(
zenity --entry --text "Choose an action"
)
rc=$?
done
This loop will exit if you enter End in the text field, or if you press the Cancel button.

Related

How to create a menu with a range of options?

I would like to create a menu in shell script with a range of options. In this case, I want to force the user only to use a range of numbers, e.g. 1 to 5, but without use CASE. If the user choose 6, the menu ask again for the number between the range.
I remember something like:
OPTION (){
[[ $option = +(1|2|3|4|5) ]] || OPTION
}
Following may help you in same:
cat choose2.ksh
check() {
while [ ! ${finished} ]
do
echo "Please enter a digit:"
read value
if [[ $value -le 5 ]]
then
echo "user entered between 1 to 5."
finished=1
else
echo "user entered more than 5 in this case."
fi
done
}
check
Execution of script:
./choose2.ksh
Please enter a digit:
12
user entered more than 5 in this case.
Please enter a digit:
12
user entered more than 5 in this case.
Please enter a digit:
1
user entered between 1 to 5.
So you could see that if user enters other than 1 to 5 than it asks user again a Input else it will simple come out of script(you could do other things too as per your need).

Create interrupt in bash by keys like ENTER Or ESC

I need to know is it possible to interrupt a bash script using keys like ESC or ENTER? By sending SIGINT /CTRL + C I am able to do, but due to some reasons(Check note in the last line) I cannot use CTRL +C. So I need to have some custom way to cause an interrupt.
In other terms: In following script, cleanup function is called when CTRL + C is pressed. Now need to modify this behavior so that cleanup function should be called when some keys like ENTER OR ESC is pressed.
cleanup() {
#do cleanup and exit
echo "Cleaning up..."
exit;
}
echo "Please enter your input:"
read input
while true
do
echo "This is some other info MERGED with user input in loop + $input"
sleep 2;
echo "[Press CTRL C to exit...]"
trap 'cleanup' SIGINT
done
Query:
Is it possible to use custom keys for causing interrupts in bash?
If possible, how to achieve it?
Note:
Reason: This script is called from another C++ program which has its own trap handling. So the trap handling of this script is conflicting with the parent program and ultimately the terminal is getting hung. In my organization that program's code is frozen so I cannot change its behavior. I have to tweak this child script only.
Here is a dirty trick which is doing my work just fine. read and case statement options are the key. Here I am timing out read command so that while true continues unless esc or enter is pressed.
cleanup() {
#do cleanup and exit
echo "Cleaning up..."
exit;
}
exitFunction()
{
echo "Exit function has been called..."
exit 0;
}
mainFunction()
{
while true
do
echo "This is some other info MERGED with user input in loop"
IFS=''
read -s -N 1 -t 2 -p "Press ESC TO EXIT or ENTER for cleanup" input
case $input in
$'\x0a' ) cleanup; break;;
$'\e' ) exitFunction;break;;
* ) main;break;;
esac
done
}
mainFunction
Following works ^M for ENTER and ^[ for ESC but may
stty intr ^M
stty intr ^[
but after cannot use ENTER
to restore default
stty intr ^C
After comment, as the shell is interactive what about asking to continue instead using trap, also clean can be done in special EXIT trap.
How do I prompt for Yes/No/Cancel input in a Linux shell script?
or yet another solution using select
echo "Do you want to continue?"
PS3="Your choice: "
select number in Y N;
do
case $REPLY in
"N")
echo "Exiting."
exit
;;
"Y")
break
;;
esac
done
# continue

Simple exit script (if - then )

I'm so new to programming it hurts! I'm trying to make a very very basic exit out of the termial script. Basically you run the scipt. It will then ask if you want to exit terminal or stay. When I choose either 1 or 2, it just returns to the terminal.
Ideally, I wanted to type "logout" and I would get the same script with the same options, but I've been trying to 2 days and it doesn't make sense.
Thank you in adavance!
#! /bin/bash
# Created by Sarge on Feb. 9, 2015
#This script locks the screen when user logs out
# User must type "logout" and will then have to enter their password
# After the correct password for the user is enter
# the screen will go into lock mode
# clear the screen
clear
# exit options
echo "1. Exit"
echo "2. No"
# exit screen
echo "Exit terminal? (1 = Exit. 2 = No)."
# user input
read user_input
if [ $user_input = 1 ] ; then
exit
elif [ $user_input = 2 ] ; then
exit 0
fi
Thank you again.
The script is run in a different process than the interactive shell. exit therefore ends just the script. Note that the shell may not be what logged the user in and terminating it won't logout the user (eg. when the user runs shell within his shell). If it is enough for you to kill the shell, you can run kill $PPID.
It is quite hard to find what exactly logged the user in. The best approximation would be to climb the process tree up until the parent process is no longer running under the user you want to logout, then kill the child process (or all processes in this subtree to be sure nothing survives). You can use pstree to visualize what you need to do and ps to actually do it.
use == not = operator like so:
if [ $user_input == 1 ] ; then
exit
elif [ $user_input == 2 ] ; then
exit 0
fi
you may also choose to replace this with -eq like so:
if [ $user_input -eq 1 ] ; then
exit
elif [ $user_input -eq 2 ] ; then
exit 0
fi
Reason:
In most programming languages including BASH,
= is known as the assignment operator that assigns value on the right side to the variable on the left side
== is the equality check operator that checks for equality of LHS and RHS and returns either true or false based on comparison result
(This is what is done in the if condition in this program).
Figured out how to run off a key word. Created a bin folder in my main directory. I put my script in bin. Than I tried Alias = "end" = "logout". Now when I just type "end" (no quotes), my script automatically runs!!! Whoop!

Bash submenus (select opt in)

I am trying to make a complete BASH menu with submenus via select opt in.
The problem : When I go to a submenu then come back to the initial menu, it do not show options.
----------------------------------------------
Greenwatch's Kiosk debug menu
----------------------------------------------
1) Keyboard Layout, 5) Configure Kiosk's password,
2) Timezone configuration, 6) Set Proxy,
3) -, 7) Remove Proxy
4) Launch Kiosk anyway,
Enter your choice (mainmenu), press 0 to reboot: 1
1) Azerty layout (BE)
2) Querty layout (US)
3) Cancel
Enter your choice (submenu): 1
AZERTY Keyboard configured
Enter your choice (mainmenu), press 0 to reboot:
This is the code(simplified -with only one submenu- )
choose_keyboard() {
show_title "Choose your keyboard layout"
clear;
select opt in "Azerty layout (BE)" "Querty layout (US)" "Cancel"; do
case "$REPLY" in
1 ) loadkeys be-latin1; echo "AZERTY Keyboard configured"; break;;
2 ) loadkeys us; echo "QWERTY Keyboard configured"; break;;
3 ) echo "Canceled"; break;;
777 ) break;;
*) echo "This is not a valid option, retry";;
esac
done
}
main_menu() {
show_title "$title"
select opt in "${options[#]}"; do
case "$REPLY" in
0 ) show_title "See you as late as possible!"; sudo systemctl reboot;;
1 ) choose_keyboard;;
2 ) choose_timezone;;
3 ) lauch_kiosk;;
4 ) choose_password;;
5 ) choose_ipconfig;;
6 ) choose_proxy;;
7 ) choose_testlab;;
777 ) break;;
*) echo "This is not a valid option, retry";;
esac
done
}
main_menu
How could I force select to display the menu?
NOTE: If I call main_menu into the choose_keyboard function, I will certainly obtain a stackoverflow error!
When you break from the inner select, you re-enter the top (main menu) select - as you have discovered, the menu isn't displayed because you don't re-execute the commands at the beginning of the function. Instead, you can break out of the inner and outer selects at once, and have the main menu in a loop so that it gets called again, ie:
1 ) loadkeys be-latin1; echo "AZERTY Keyboard configured"; break 2;;
break 2 will break out of a select nested inside another, break 3 will break out of an additional level of nesting, etc. Then instead of just calling main_menu at the bottom, do something like:
while :; do main_menu; done
This is an infinite loop which will call main_menu whenever you break out of the main menu select command. You may not want it to be infinite, you can always test against a variable or something there.

Is there a "goto" statement in bash?

Is there a "goto" statement in bash ? I know It is considered bad practice, but I need specifically "goto".
If you are using it to skip part of a large script for debugging (see Karl Nicoll's comment), then if false could be a good option (not sure if "false" is always available, for me it is in /bin/false):
# ... Code I want to run here ...
if false; then
# ... Code I want to skip here ...
fi
# ... I want to resume here ...
The difficulty comes in when it's time to rip out your debugging code. The "if false" construct is pretty straightforward and memorable, but how do you find the matching fi? If your editor allows you to block indent, you could indent the skipped block (then you'll want to put it back when you're done). Or a comment on the fi line, but it would have to be something you'll remember, which I suspect will be very programmer-dependent.
No, there is not; see ยง3.2.4 "Compound Commands" in the Bash Reference Manual for information about the control structures that do exist. In particular, note the mention of break and continue, which aren't as flexible as goto, but are more flexible in Bash than in some languages, and may help you achieve what you want. (Whatever it is that you want . . .)
It indeed may be useful for some debug or demonstration needs.
I found that Bob Copeland solution http://bobcopeland.com/blog/2012/10/goto-in-bash/ elegant:
#!/bin/bash
# include this boilerplate
function jumpto
{
label=$1
cmd=$(sed -n "/$label:/{:a;n;p;ba};" $0 | grep -v ':$')
eval "$cmd"
exit
}
start=${1:-"start"}
jumpto $start
start:
# your script goes here...
x=100
jumpto foo
mid:
x=101
echo "This is not printed!"
foo:
x=${x:-10}
echo x is $x
results in:
$ ./test.sh
x is 100
$ ./test.sh foo
x is 10
$ ./test.sh mid
This is not printed!
x is 101
You can use case in bash to simulate a goto:
#!/bin/bash
case bar in
foo)
echo foo
;&
bar)
echo bar
;&
*)
echo star
;;
esac
produces:
bar
star
If you're testing/debugging a bash script, and simply want to skip forwards past one or more sections of code, here is a very simple way to do it that is also very easy to find and remove later (unlike most of the methods described above).
#!/bin/bash
echo "Run this"
cat >/dev/null <<GOTO_1
echo "Don't run this"
GOTO_1
echo "Also run this"
cat >/dev/null <<GOTO_2
echo "Don't run this either"
GOTO_2
echo "Yet more code I want to run"
To put your script back to normal, just delete any lines with GOTO.
We can also prettify this solution, by adding a goto command as an alias:
#!/bin/bash
shopt -s expand_aliases
alias goto="cat >/dev/null <<"
goto GOTO_1
echo "Don't run this"
GOTO_1
echo "Run this"
goto GOTO_2
echo "Don't run this either"
GOTO_2
echo "All done"
Aliases don't usually work in bash scripts, so we need the shopt command to fix that.
If you want to be able to enable/disable your goto's, we need a little bit more:
#!/bin/bash
shopt -s expand_aliases
if [ -n "$DEBUG" ] ; then
alias goto="cat >/dev/null <<"
else
alias goto=":"
fi
goto '#GOTO_1'
echo "Don't run this"
#GOTO1
echo "Run this"
goto '#GOTO_2'
echo "Don't run this either"
#GOTO_2
echo "All done"
Then you can do export DEBUG=TRUE before running the script.
The labels are comments, so won't cause syntax errors if disable our goto's (by setting goto to the ':' no-op), but this means we need to quote them in our goto statements.
Whenever using any kind of goto solution, you need to be careful that the code you're jumping past doesn't set any variables that you rely on later - you may need to move those definitions to the top of your script, or just above one of your goto statements.
Although others have already clarified that there is no direct goto equivalent in bash (and provided the closest alternatives such as functions, loops, and break), I would like to illustrate how using a loop plus break can simulate a specific type of goto statement.
The situation where I find this the most useful is when I need to return to the beginning of a section of code if certain conditions are not met. In the example below, the while loop will run forever until ping stops dropping packets to a test IP.
#!/bin/bash
TestIP="8.8.8.8"
# Loop forever (until break is issued)
while true; do
# Do a simple test for Internet connectivity
PacketLoss=$(ping "$TestIP" -c 2 | grep -Eo "[0-9]+% packet loss" | grep -Eo "^[0-9]")
# Exit the loop if ping is no longer dropping packets
if [ "$PacketLoss" == 0 ]; then
echo "Connection restored"
break
else
echo "No connectivity"
fi
done
This solution had the following issues:
Indiscriminately removes all code lines ending in a :
Treats label: anywhere on a line as a label
Here's a fixed (shell-check clean and POSIX compatible) version:
#!/bin/sh
# GOTO for bash, based upon https://stackoverflow.com/a/31269848/5353461
goto() {
label=$1
cmd=$(sed -En "/^[[:space:]]*#[[:space:]]*$label:[[:space:]]*#/{:a;n;p;ba};" "$0")
eval "$cmd"
exit
}
start=${1:-start}
goto "$start" # GOTO start: by default
#start:# Comments can occur after labels
echo start
goto end
# skip: # Whitespace is allowed
echo this is usually skipped
# end: #
echo end
There is one more ability to achieve a desired results: command trap. It can be used to clean-up purposes for example.
There is no goto in bash.
Here is some dirty workaround using trap which jumps only backwards:)
#!/bin/bash -e
trap '
echo I am
sleep 1
echo here now.
' EXIT
echo foo
goto trap 2> /dev/null
echo bar
Output:
$ ./test.sh
foo
I am
here now.
This shouldn't be used in that way, but only for educational purposes. Here is why this works:
trap is using exception handling to achieve the change in code flow.
In this case the trap is catching anything that causes the script to EXIT. The command goto doesn't exist, and hence throws an error, which would ordinarily exit the script. This error is being caught with trap, and the 2>/dev/null hides the error message that would ordinarily be displayed.
This implementation of goto is obviously not reliable, since any non-existent command (or any other error, for that manner), would execute the same trap command. In particular, you cannot choose which label to go-to.
Basically in real scenario you don't need any goto statements, they're redundant as random calls to different places only make your code difficult to understand.
If your code is invoked many times, then consider to use loop and changing its workflow to use continue and break.
If your code repeats it-self, consider writing the function and calling it as many times as you want.
If your code needs to jump into specific section based on the variable value, then consider using case statement.
If you can separate your long code into smaller pieces, consider moving it into separate files and call them from the parent script.
I found out a way to do this using functions.
Say, for example, you have 3 choices: A, B, and C. A and Bexecute a command, but C gives you more info and takes you to the original prompt again. This can be done using functions.
Note that since the line containg function demoFunction is just setting up the function, you need to call demoFunction after that script so the function will actually run.
You can easily adapt this by writing multiple other functions and calling them if you need to "GOTO" another place in your shell script.
function demoFunction {
read -n1 -p "Pick a letter to run a command [A, B, or C for more info] " runCommand
case $runCommand in
a|A) printf "\n\tpwd being executed...\n" && pwd;;
b|B) printf "\n\tls being executed...\n" && ls;;
c|C) printf "\n\toption A runs pwd, option B runs ls\n" && demoFunction;;
esac
}
demoFunction
This is a small correction of the Judy Schmidt script put up by Hubbbitus.
Putting non-escaped labels in the script was problematic on the machine and caused it to crash. This was easy enough to resolve by adding # to escape the labels. Thanks to Alexej Magura and access_granted for their suggestions.
#!/bin/bash
# include this boilerplate
function goto {
label=$1
cmd=$(sed -n "/$#label#:/{:a;n;p;ba};" $0 | grep -v ':$')
eval "$cmd"
exit
}
start=${1:-"start"}
goto $start
#start#
echo "start"
goto bing
#boom#
echo boom
goto eof
#bang#
echo bang
goto boom
#bing#
echo bing
goto bang
#eof#
echo "the end mother-hugger..."
A simple searchable goto for the use of commenting out code blocks when debugging.
GOTO=false
if ${GOTO}; then
echo "GOTO failed"
...
fi # End of GOTO
echo "GOTO done"
Result is-> GOTO done
My idea for creating something like "goto" is to use select with case and assign a variable, which I then check in an if statement. Not perfect, but may help in some cases
Example:
#!/usr/bin/env bash
select goto in Ubuntu Debian Quit ; do
case $goto in
Ubuntu) { CHOICE="Ubuntu" ; break ; } ;;
Debian) { CHOICE="Debian" ; break ; } ;;
Quit) { echo "Bye" ; exit ; } ;;
*) { echo "Invalid selection, please try again..." ; } ;;
esac
done
if [ "$CHOICE" == "Ubuntu" ]; then
echo "I'm in Ubuntu"
fi
if [ "$CHOICE" == "Debian" ]; then
echo "I'm in Debian"
fi
Why don't anyone just use functions directly ?
BTW functions are easier to deal with than making a new thing
My style :
#!/bin/bash
# Your functions
function1 ()
{
commands
}
function2 ()
{
commands
}
:
:
functionn ()
{
commands
}
# Execute 1 to n in order
for i in {1..n}
do
function$i
done
# with conditions
for i in {1..n}
do
[ condition$i ] && function$i
done
# Random order
function1
functionn
function5
:
:
function3
Example for above style :
#!/bin/bash
# Your functions
function1 ()
{
echo "Task 1"
}
function2 ()
{
echo "Task 2"
}
function3 ()
{
echo "Task 3"
}
function1
function3
function2
Output :
Task 1
Task 3
Task 2
Drawbacks :
Script in an organized way.
Less problems and not prone to errors.
You can make function inside a existing function.
Move back and forth without any problems.

Resources