Can a YAD button invoke a function within a script? - bash

I am playing around with YAD dialogs in BASH and am having trouble with the button construction. I can't get a YAD button to call a function in the same script. Is there a way to do this?
My understanding is the if I use the following construction, pressing the button will call the command that follows the colon This example (which works) will open an instance of Firefox if a user clicks the Open Browser button:
yad --button="Open browser":firefox
I have a script with several BASH functions. I'd like a button press to call one of the functions. It doesn't. The following is a simple script that, when run, demonstrates the disappointing behavior:
#!/bin/bash,
click_one()
{
yad --center --text="Clicked the one"
}
click_two()
{
yad --center --text="Clicked the two"
}
cmd="yad --center --text=\"Click a button to see what happens\" \
--button=\"One\":click_one \
--button=\"Two\":2 \
--button=\"Date\":date \
--button=\"Exit\":99"
proceed=true
while $proceed; do
eval "$cmd"
exval=$?
case $exval in
2) click_two;;
99) proceed=false;;
esac
done
In the code above, button Date works as expected, calling the date command. Buttons Two and Exit work because I'm checking the exit value of the command and branching on its value. Sadly (for me), button One does nothing. I had hoped that clicking on button One would call the local function click_one. I would like to know if there's a way to format the YAD command so that the click_one function is called.
While the above code suggests a workaround using the exit value, my real goal is to apply a successful answer to a form button that, as far as I can figure out so far, doesn't return an exit value. In other words, the following also fails silently, but I'd like it to invoke function click_one:
yad --form --field="One":fbtn click_one

a possible way:
#!/bin/bash
click_one(){
yad --center --text="Clicked the one"
}
click_two(){
yad --center --text="Clicked the two"
}
export -f click_one click_two
yad \
--title "My Title" \
--center --text="Click a button to see what happens" \
--button="One":"bash -c click_one" \
--button="Two":"bash -c click_two" \
--button="Date":"date" \
--button="Exit":0
echo $?

Apparently not, it needs to be an actual command.
You could: put your functions in a separate file and as the command, launch bash, source that file, and call the function.
Here, I'm also restructuring your code to store the yad command in an array. This will make your script more robust:
# using an array makes the quoting a whole lot easier
cmd=(
yad --center --text="Click a button to see what happens"
--button="One":"bash -c 'source /path/to/functions.sh; click_one'"
--button="Two":2
--button="Date":date
--button="Exit":99
)
while true; do
"${cmd[#]}" # this is how to invoke the command from the array
exval=$?
case $exval in
2) click_two;;
99) break;;
esac
done

Related

Displaying an Xcode variable in a shell script in an alert fails to display the alert

I have a shell script that I run from Xcode to open the root folder in the Terminal. It works as expected when invoked by a function key in Xcode and opens a Terminal window pointing to the value of $SRCROOT as defined in Xcode and an alert with the expected message appears before that.
#! /bin/bash
osascript -e 'display dialog "message" with title "Hi"'
open -a Terminal "$SRCROOT"
Yet when I try to replace "message" to display the contents of $SRCROOT, the dialog doesn't display at all. I've tried all of the approaches listed in the solutions here: osascript using bash variable with a space
Using any of those approaches results in the alert not displaying at all.
I even tried to display the contents of $SRCROOT in a notification with various methods of escaping it but that just displays an empty notification.
Any ideas? TIA.
Save following in test.sh :
#!/usr/bin/env bash
SRCROOT=~/Developer/SwiftVC
osascript \
-e "on run(argv)" \
-e 'display dialog item 1 of argv with title "Hi"' \
-e "end" \
-- "$SRCROOT"
open -a Terminal "$SRCROOT"
and run it with :
chmod +x test.sh
./test.sh

How to confirm an input using Zenity?

I need to display a GUI pop up that shows a message with a variable, and allows user to confirm or cancel.
Perhaps I'm missing something, but I could not find any documentation on Zenity --questions tag. So far I couldn't get anything other than default question to display.
I don't want to use Yad if possible... I would like to stick with Zenity.
Is there any way to do this with Zenity?
Example:
var="ABC"
msg="Confirm $var"
zenity --text-info \
--title="Confirm"
--text="$msg"
# --question "This is the question"
case $? in
0)
return
;;
1)
return
;;
esac
zenity --question --title="Confirm" --text="$msg"
It's --question, not --questions, but in the Zenity manual, do a Ctrl-F for question to find all the relevant sections.

Is there a way for a bash script to re-run after a case selection?

I just wrote a bash script to change PHP-cli and Apache PHP versions without having to manually run dismod or enmod etc etc, and for the script to restart Apache to make the changes, and being that the script uses a case statement, I have a problem in that when the code inside the choice is finished the entire script ends, ie:-
case $CHOICE in
1) do code;;
2) do code;;
esac
So when either choice of 1 or 2 is chosen, the script inside choices is executed, then the script is ended, but I want to restart the script unless the 'Cancel' button is pressed in the dialog box, ie:-
CHOICE=$(dialog --clear \
--backtitle "$BACKTITLE" \
--title "$TITLE" \
--menu "$MENU" \
$HEIGHT $WIDTH $CHOICE_HEIGHT \
"${OPTIONS[#]}" \
2>&1 >/dev/tty)
I have tried adding the script name as an extra line before the ;; but it still stops.
To give an idea, the code I have written is to big to post here, but I'll post it to pastebin and post the link here:-
https://pastebin.com/q2iH7Q2E
As can be seen, I had to add extra case lines at the end of the script, but I want to remove those and have the script rerun after any of the selections are made.
Put a loop around all the code.
while :
do
# all the code that should repeat goes here
done
Inside the loop you can use break to stop the loop.

Why isn't this command returning to shell after &?

In Ubuntu 14.04, I created the following bash script:
flock -nx "$1" xdg-open "$1" &
The idea is to lock the file specified in $1 (flock), then open it in my usual editor (xdg-open), and finally return to prompt, so I can open other files in sequence (&).
However, the & isn't working as expected. I need to press Enter to make the shell prompt appear again. In simpler constructs, such as
gedit test.txt &
it works as it should, returning the prompt immediately. I think it has to do with the existence of two commands in the first line. What am I doing wrong, please?
EDIT
The prompt is actually there, but it is somehow "hidden". If I issue the command
sudo ./edit error.php
it replies with
Warning: unknown mime-type for "error.php" -- using "application/octet-stream"
Error: no "view" mailcap rules found for type "application/octet-stream"
Opening "error.php" with Geany (application/x-php)
__
The errors above are not related to the question. But instead of __ I see nothing. I know the prompt is there because I can issue other commands, like ls, and they work. But the question remains: WHY the prompt is hidden? And how can I make it show normally?
Why isn't this command returning to shell after &?
It is.
You're running a command in the background. The shell prints a new prompt as soon as the command is launched, without waiting for it to finish.
According to your latest comment, the background command is printing some message to your screen. A simple example of the same thing:
$ echo hello &
$ hello
The cursor is left at the beginning of the line after the $ hello.
As far as the shell is concerned, it's printed a prompt and is waiting a new command. It doesn't know or care that a background process has messed up your display.
One solution is to redirect the command's output to somewhere other than your screen, either to a file or to /dev/null. If it's an error message, you'll probably have to redirect both stdout and `stderr.
flock -nx "$1" xdg-open "$1" >/dev/null 2>&1 &
(This assumes you don't care about the content of the message.)
Another option, pointed out in a comment by alvits, is to sleep for a second or so after executing the command, so the message appears followed by the next shell prompt. The sleep command is executed in the foreground, delaying the printing of the next prompt. A simple example:
$ echo hello & sleep 1
hello
[1] + Done echo hello
$
or for your example:
flock -nx "$1" xdg-open "$1" & sleep 1
This assumes that the error message is printed in the first second. That's probably a valid assumption for you example, but it might not be in general.
I don't think the command is doing what you think it does.
Have you tried to run it twice to see if the lock cannot be obtained the second time.
Well, if you do it, you will see that it doesn't fail because xdg-open is forking to exec the editor. Also if it fails you expect some indication.
You should use something like this
flock -nx "$1" -c "gedit '$1' &" || { echo "ERROR"; exit 1; }

Hide dialog output in bash scrollback (Ubuntu)

I use the following script to display a dialog in the bash shell:
#!/bin/bash
TFILE=/tmp/habitat_resp_`whoami`.$$
dialog --menu "Commander?" 20 50 10 \
1 "MySQL" \
2 "Apache" \
3 "Postfix" \
4 "Dovecot" \
5 "Owncloud" \
2> $TFILE
# get response
RESPONSE=$(cat $TFILE)
echo $RESPONSE
clear
The problem is, when I scroll up, i can still see the dialog in my scrollback. I want it like vi. I open my script and the dialog appears and if the script is over you cant see the dialog in scrollback.
How can this be achieved?
Regards
S.
Add the --keep-tite option, which tells dialog to use the "alternative screen", which doesn't participate in terminal scrollback. This produces some slightly annoying display artefacts, but possibly not as annoying as polluting your scrollback.

Resources