View previous topic :: View next topic |
Author |
Message |
roytheman Tux's lil' helper
Joined: 08 Nov 2009 Posts: 102
|
Posted: Fri Apr 18, 2014 9:32 pm Post subject: Bash script to toggle between launching and closing program |
|
|
Hello Gentoo forum,
I am looking for a bash script that I can place on my desktop as an icon, and I want it to start a program, like kcalc, for example (KDE calculator), by clicking on it once and I want it to stop (kill) the calculator when I click on that same icon again. In other words, if I click on the icon once, it starts kcalc, click on it again, it stops kcalc, click on it again, it starts, and so forth. I can already accomplish this with two separate bash script icons on my desktop, one for start only and one for stop only. But I want only one icon that will do both, that will toggle between launching a program and killing the program by clicking on it each time.
I have Googled for one but all I could find were mostly init scripts.
I plan to use this script to stop and start wpa_supplicant (uses pids), click once and I'm online and click again and I offline, click again and I'm online, etc.
I have very little experience with the bash programming language (but I find it quite interesting), and I hope someone might be kind enough to help me find what I am looking for.
Sincerely,
Roy
PS - I found one that looks like it would work with kcalc but it does nothing.
Code: |
#!/bin/sh
case "$1" in
start)
/usr/bin/kcalc
;;
stop)
kill `pgrep -x kcalc'` > /dev/null 2>&1
;;
*)
echo "usage: $0 {start|stop}"
;;
esac
|
|
|
Back to top |
|
|
cboldt Veteran
Joined: 24 Aug 2005 Posts: 1046
|
Posted: Fri Apr 18, 2014 11:06 pm Post subject: |
|
|
Generally, the script is going to be performing a conditional operation.
Code: | if [ test for program_is_running ]; then
kill_program
optionally remove the PID file or unset the flag
else
start_program
optionally create a PID file or set a flag
fi |
There is more than one way to test for program running. I use this in a script to start alpine ...
Code: | if [ -n "`pgrep -u $USER alpine`" ] |
The `pgrep -u $USER alpine` command has output if the user running the command has an instance of the program "alpine" running on the system. The "-n" test is met if the sting being testing has non-zero length.
Alternatives would be to test for the presence of a PID file, e.g. in /var/run (not all programs put an entry there, but the script could make one there), and setting a flag in the window-manager. Here is an example of flag-setting using fvwm, no bash scripting is involved in this toggle ...
Code: | DestroyFunc TOGGLE_STICKY
AddToFunc TOGGLE_STICKY
+ I Test (EnvMatch Sticky_Status ON) Function STOP_STICKY
+ I TestRc (NoMatch) Function START_STICKY
DestroyFunc START_STICKY
AddToFunc START_STICKY
+ I (do some stuff)
+ I SetEnv Sticky_Status ON
DestroyFunc STOP_STICKY
AddToFunc STOP_STICKY
+ I (do some stuff)
+ I SetEnv Sticky_Status OFF | [/code] |
|
Back to top |
|
|
roytheman Tux's lil' helper
Joined: 08 Nov 2009 Posts: 102
|
Posted: Fri Apr 18, 2014 11:23 pm Post subject: |
|
|
Thanks for the reply cboldt,
I'll have to study what you showed me but I found a simple one without pids that works with kcalc :
Code: |
#!/bin/sh
# kcalc-toggle
if pgrep -x kcalc > /dev/null; then
exec killall kcalc
else
exec kcalc
fi
|
What do you think of this one? |
|
Back to top |
|
|
cboldt Veteran
Joined: 24 Aug 2005 Posts: 1046
|
Posted: Fri Apr 18, 2014 11:38 pm Post subject: |
|
|
That would do the job, it's a toggle. It tests for kcalc running, using pgrep. If pgrep returns success (finds kcalc running), then kcalc is killed. Otherwise, kcalc is started.
The "> /dev/null" part stifles the output of pgrep, but the exit status of pgrep ("success" if kcalc is running, "failure" if kcalc is not running) can be tested even though the output is redirected to the bitbucket.
Your other example script needed a parameter to work, like an init script does. The command would have to be "kcalc start" or "kcalc stop", and you want a script that learns or remembers if kcalc is running, not needing to be told "start" or "stop". |
|
Back to top |
|
|
khayyam Watchman
Joined: 07 Jun 2012 Posts: 6227 Location: Room 101
|
Posted: Sat Apr 19, 2014 12:04 am Post subject: Re: Bash script to toggle between launching and closing prog |
|
|
roytheman wrote: | I plan to use this script to stop and start wpa_supplicant (uses pids), click once and I'm online and click again and I offline, click again and I'm online, etc. |
roytheman ... I wouldn't do that (at least in a similar manner to the kcalc script above). Your best to use the /etc/init.d script and check for a exit status of the 'status' ... eg:
Code: | #!/bin/sh
/etc/init.d/net.wlan0 status >/dev/null 2>&1
if [ "$?" -eq 0 ] ; then
/etc/init.d/net.wlan0 stop
else
/etc/init.d/net.wlan0 start
fi |
... you can check for the 'exit status' of commands like so ...
Code: | # /etc/init.d/net.wlan0 status >/dev/null 2>&1 ; echo $?
0
# /etc/init.d/wpa_supplicant status >/dev/null 2>&1 ; echo $?
3 |
"0" equates to "success" (meaning a successful exit status) any other number means "failure" (of some sort) in the example above wpa_supplicant returns "3" as the service isn't started.
Really though, you should use a manager (like wpa_gui) for such things ... so I provide the above meerly to explain the scripting involved.
HTH & best ... khay |
|
Back to top |
|
|
krinn Watchman
Joined: 02 May 2003 Posts: 7470
|
Posted: Sat Apr 19, 2014 1:09 am Post subject: Re: Bash script to toggle between launching and closing prog |
|
|
khayyam wrote: | roytheman wrote: | I plan to use this script to stop and start wpa_supplicant (uses pids), click once and I'm online and click again and I offline, click again and I'm online, etc. |
roytheman ... I wouldn't do that (at least in a similar manner to the kcalc script above). Your best to use the /etc/init.d script and check for a exit status of the 'status' ... eg:
|
Agree with khayyam, because killing a running init script will push it to crash state.
And /etc/init.d/script zap must be done prior to rerun it.
And your one click will be: click->start, click->kill, click->nothing... |
|
Back to top |
|
|
roytheman Tux's lil' helper
Joined: 08 Nov 2009 Posts: 102
|
Posted: Sat Apr 19, 2014 3:53 pm Post subject: |
|
|
Hello again,
You guys have really helped and inspired me in this matter.
khayyam's script works wonderfully for starting the wireless interface but I could not get it to work with a simpler program like kcalc or wpa_gui without it starting another instance of the program each time I clicked on the icon, so I had to use a combination of both scripts posted above for it to start the wireless interface and wpa_gui as shown below:
Code: |
#!/bin/sh
sudo /etc/init.d/net.wlp2s0 status > /dev/null 2>&1
if [ "$?" -eq 0 ] ; then
sudo /etc/init.d/net.wlp2s0 stop
else
sudo /etc/init.d/net.wlp2s0 start
fi
if pgrep -x wpa_gui > /dev/null; then
exec killall wpa_gui
else
exec wpa_gui &
fi
|
As shown above, I can both launch the wireless interface AND start wpa_gui (the graphical interface) with the first mouse click ,and when I click once again on the icon script, both the wireless interface and wpa_gui closes at the same time. So I can toggle between starting both and closing both at the same time. I like that.
What I would really like is for the scripts to work with the pids. I believe both wpa_gui and the wireless interface have their own pids but they are not directly used in these scripts.
And in order for the script icons to work as a regular user being on the desktop, the suder's file must be edited similar as shown below (for those who might not know that):
Code: |
royroy ALL=(root) NOPASSWD: /etc/init.d/net.wlp2s0
royroy ALL=(root) NOPASSWD: /etc/init.d/net.eth0
royroy ALL=(root) NOPASSWD: /dev/null
royroy ALL=(root) NOPASSWD: /usr/sbin/wpa_supplicant
|
Thank you, cboldt, khayyam, and krinn for help me in this matter.
Best regards,
Roy |
|
Back to top |
|
|
mv Watchman
Joined: 20 Apr 2005 Posts: 6747
|
Posted: Sat Apr 19, 2014 4:59 pm Post subject: |
|
|
roytheman wrote: | Code: | sudo /etc/init.d/net.wlp2s0 status > /dev/null 2>&1
if [ "$?" -eq 0 ] ; then |
|
Just an editorial comment: You can write this as
If you want to store the PID: Code: |
wpa_gui &
wpa_pid=$!
printf '%s' "${wpa_pid}" >/run/wpa.pid |
(Note that you must be root to write to that file; see my comments below)
To read the PID: Code: | if test -r /run/wpd.pid && read -r wpa_pid </runwpa.pid
then kill ... && rm /run/wpd.pid
fi | or use Code: | tesst -r ... && pkill -F ... && rm ... |
Quote: | Code: |
royroy ALL=(root) NOPASSWD: /etc/init.d/net.wlp2s0
royroy ALL=(root) NOPASSWD: /etc/init.d/net.eth0
royroy ALL=(root) NOPASSWD: /dev/null
royroy ALL=(root) NOPASSWD: /usr/sbin/wpa_supplicant
|
|
This is a horrible idea: It means royroy can start this scripts/tools with any argument! (BTW: what has /dev/null lost here?) For instance, if you start a mail program or browser (as user royroy) which has an exploit, somebody might use this exploit to call wpa_supplicant with appropriate parameters to get some sensitive information!
A better way is to start your whole script with sudo and to allow royroy only to use that script (use sudo or su inside the script to start wpa_supplicant with user permissions instead of root permissions). If you follow this suggestion, you can simply write to /run/wpd.pid as in the above code (otherwise you would need to call a shell with sudo to do the redirection) |
|
Back to top |
|
|
khayyam Watchman
Joined: 07 Jun 2012 Posts: 6227 Location: Room 101
|
Posted: Sun Apr 20, 2014 2:24 am Post subject: |
|
|
roytheman ...
A couple of comments, firstly, I really don't think this is the proper way to approach this, if you want a method of starting/stopping the network via a click then use some 'manager', because (as mv points to above) your shifting things into a realm where you're executing things as superuser when there is no need to. For example, wpa_supplicant has a method of allowing users access to the ctrl_interface and so the wpa_supplicant process, by using this no escalation of privileges is required, and wpa_gui can be used to start/stop the network (which, incidentally, doesn't need to be killed and restarted when the network is brought up/down). To do this all you need do is allow the user access to the ctrl_interface ...
/etc/wpa_supplicant/wpa_supplicant.conf
Code: | ctrl_interface=DIR=/run/wpa_supplicant GROUP=wheel
update_config=1 |
... here to group 'wheel' is used (which I assume your user is in) but a separate group could be created if need be.
Secondly, in the script above you're starting/stopping net.wlan0 (as per my example) but this will also start/stop anything dependent on 'net'. Really, all that is needed is that wpa_supplicant connect/disconnect (which due to /etc/wpa_supplicant/wpa_cli) will call /etc/init.d/net.wlan0 (and so have dhcpcd started, etc). So, using the method provided by wpa_supplicant is far better in this regard as all the process/PID checking is done ... and you have wpa_gui as your means of control.
Thirdly, the use of 'sudo' for such things should be avoided ... as I explained above there is no privilege escalation required, and as mv pointed out its an abuse (indeed dangerous) to allow any more escalation of privileges than is required. I see (or have seen) all manner of scripts and instructions (normally with the word 'ubuntu' attached) where every command is prefaced with sudo ... its a bad habit and should be checked ... if not avoided altogether (such as in scripts!).
So, though you have based the above on the information I provided, I should note that I did state that this was given to show you what was involved, but I didn't (and don't) recommend this.
best ... khay |
|
Back to top |
|
|
khayyam Watchman
Joined: 07 Jun 2012 Posts: 6227 Location: Room 101
|
Posted: Sun Apr 20, 2014 1:31 pm Post subject: |
|
|
roytheman ...
An afterthought: I think something more along these lines would be better ...
Code: | #!/bin/sh
wpa_cli ping >/dev/null 2>&1
if [ "$?" -gt 0 ]; then
# we do nothing as wpa_supplicant isn't running
exit
fi
status=`wpa_cli status |awk -F= '/^wpa_state/{v=1; if ($2 == "COMPLETED") v=0; print v}'`
if [ $status -eq 0 ]; then
wpa_cli disconnect
else
wpa_cli reconnect
fi |
It requires that net.wlan0 be started, but once started the network would be managed/toggled via the script ... and no sudo is needed (you would of course need to make sure you've configured wpa_supplicant to allow your user access to the ctrl_interface).
Its untested so consider it just an example ... its basically operating much the same as wpa_gui would.
best ... khay |
|
Back to top |
|
|
roytheman Tux's lil' helper
Joined: 08 Nov 2009 Posts: 102
|
Posted: Mon Apr 21, 2014 2:33 pm Post subject: |
|
|
Hello again Gentoo forum,
I configured my scripts a different way that may improve security. I commented out all of the entries in the suder's file (shown below) and I added only one line:
Code: |
#royroy ALL=(root) NOPASSWD: /etc/init.d/net.wlp2s0
#royroy ALL=(root) NOPASSWD: /etc/init.d/net.eth0
#royroy ALL=(root) NOPASSWD: /dev/null
#royroy ALL=(root) NOPASSWD: /usr/sbin/wpa_supplicant
|
Code: |
royroy ALL=(root) NOPASSWD: /usr/bin/wpa-toggle.sh
|
The script in /usr/bin (wpa-toggle.sh) shown below looks like this:
Code: |
#!/bin/sh
# wpa_supplicant-toggle
if #pgrep -x
sudo /etc/init.d/net.wlp2s0 status > /dev/null; then
sudo /etc/init.d/net.wlp2s0 stop
else
sudo /etc/init.d/net.wlp2s0 start
fi
if pgrep -x wpa_gui > /dev/null; then
exec killall wpa_gui
else
exec wpa_gui &
fi
|
And the script on my desktop (which executes the above script) looks like this:
Code: |
#!/bin/sh
sudo /usr/bin/wpa-toggle.sh
|
I had to reboot for the changes to take effect and it works fine.
I realize it may be best to use the intended init scripts in /etc/init.d to start the wired and wireless interfaces but I like clicking once on an icon and it starts and clicking again on that same icon and it stops. It is not that I'm too lazy to use the init scripts, I just like to take advantage of the power of the bash programming language whenever I can.
All the best,
Roy |
|
Back to top |
|
|
khayyam Watchman
Joined: 07 Jun 2012 Posts: 6227 Location: Room 101
|
Posted: Mon Apr 21, 2014 7:28 pm Post subject: |
|
|
roytheman wrote: | The script in /usr/bin (wpa-toggle.sh) shown below looks like this: |
roytheman ... use /usr/local/sbin for this ... /usr/ is for package installed software ...
Code: | #!/bin/sh
# wpa_supplicant-toggle
if /etc/init.d/net.wlp2s0 status >/dev/null 2>&1 ; then
/etc/init.d/net.wlp2s0 stop
else
/etc/init.d/net.wlp2s0 start
fi
# don't see why ... but ok
if pgrep -x wpa_gui >/dev/null 2>&1 ; then
killall wpa_gui
else
# run as user .. not root
DISPLAY=":0" ; su -c 'wpa_gui &' royroy
fi |
roytheman wrote: | I realize it may be best to use the intended init scripts in /etc/init.d to start the wired and wireless interfaces but I like clicking once on an icon and it starts and clicking again on that same icon and it stops. It is not that I'm too lazy to use the init scripts, I just like to take advantage of the power of the bash programming language whenever I can. |
Thats one way of thinking about it ... but I wonder why I bothered with the above.
best ... khay |
|
Back to top |
|
|
roytheman Tux's lil' helper
Joined: 08 Nov 2009 Posts: 102
|
Posted: Mon Apr 21, 2014 8:34 pm Post subject: |
|
|
Hello khayyam,
Please don't be offended due to me failing to read your post thoroughly enough. I can now see you sent considerable time and effort trying to enlighten me on bash scripts. I did try out your wpa_cli script and it did work like you said as long as the wireless interface was already started. But I would like something added to the script to bring the interface down, "like "ifconfig wlp2s0 down" but that would require root access. You see, I've got Gkrellm installed on my desktop which is a system monitor that monitors things such as the interfaces whether they are up or down and also monitors the Internet speed. When I start the wireless interface with the regular init scrips in /etc/init.d, in a root terminal, that interface will appear in Gkrellm and when I stop the wireless interface the same way, that interface in Gkrellm will disappear, making it easy for me to see if I am connected or not. But if I use wpa_cli as in the script you supplied, it will also connect me and disconnect me just fine, but the corresponding interface in Gkrellm stays put instead of disappearing. So please don't feel like what you posted was done in vain. I'm sure I'll come back to it for future reference. I'm still searching for the best method for me.
All the best,
Roy |
|
Back to top |
|
|
khayyam Watchman
Joined: 07 Jun 2012 Posts: 6227 Location: Room 101
|
Posted: Tue Apr 22, 2014 1:04 am Post subject: |
|
|
roytheman wrote: | Please don't be offended due to me failing to read your post thoroughly enough. I can now see you sent considerable time and effort trying to enlighten me on bash scripts. |
roytheman ... honestly, I'm not offended, it was just that after having made suggestions and providing code no reference was made to it, and in some places (namely the 'use of sudo in scripts') that advice was ignored (or ... as you state above ... not read thoroughly enough). I have a general policy that I'll work at a problem until a solution is found ... but if the advice is ignored, or what-have-you, I reserve the right to comment :)
roytheman wrote: | I did try out your wpa_cli script and it did work like you said as long as the wireless interface was already started. But I would like something added to the script to bring the interface down, "like "ifconfig wlp2s0 down" but that would require root access. |
... but all of your commands are run as root, you're using sudo to run the script, so that seems like a moot point. wpa_supplicant does require the interface to be marked 'up', and wpa_cli can not be run unless wpa_supplicant is running, so this seems to rule out its use.
roytheman wrote: | You see, I've got Gkrellm installed on my desktop which is a system monitor that monitors things such as the interfaces whether they are up or down and also monitors the Internet speed. When I start the wireless interface with the regular init scrips in /etc/init.d, in a root terminal, that interface will appear in Gkrellm and when I stop the wireless interface the same way, that interface in Gkrellm will disappear, making it easy for me to see if I am connected or not. But if I use wpa_cli as in the script you supplied, it will also connect me and disconnect me just fine, but the corresponding interface in Gkrellm stays put instead of disappearing. |
Yes, I see, but I really wonder at the kind of logic involved in this. I have a number of expectations about what a computer should do, the first of these is that any interface allows me to work, and second is that this interfaces doesn't get in the way of that work. If I need to know something (such as network status) I can query it, but otherwise I have no reason to have this present ... and getting "in the way". The reasoning behind this (simple) idea is that such information is something I am generally aware of, I know I'm currently in front of a keyboard, and connected to a network, etc, etc, to have this information represented to me in some manner seems obsolete (and "getting in the way"). I say that not because I'm omnipotent, but because I'm aware of the way in which an interface conditions *use* (and by extension the "design" behind that use). I never ask myself "I wonder if I'm online or not", such a thought never enters into my workflow, it would be a distraction, and the "design" (and use) makes it so. Now, when I read the above I'm somewhat surprised, because (and this is not a judgement of you in any way) the interface seems to be conditioning a certain kind of use (ok, not so much "surprised" as "puzzled").
You mentioned previously the "power of the bash programming language", I was going to comment on it then but didn't as I wasn't sure what to say, something like "with power, comes responsibility" would sound something of a platitude, but really given the mistakes made (and yes, we all make them, so this is not about skill level or what-have-you), I should ask, what power? Because, the power is in how its exercised ... and going back to the above its *use*.
So, drawing those two thoughts together, yes, there is power in (actually /bin/sh ... which is not *supposed* to be bash) shell, but power is not a servant (if that's a suitable analogy) of "use" (ie, all of the "usability" promised by the GUI, etc) but something that comes from it.
best ... khay |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|