I'm new to linux and I'm learning to script an alarm clock using bash and cron. The script works fine from bash and by double clicking on it, but not from cron; as soon as cron calls the script, the alarm sounds once (one loop, it seems) and then stops without even showing the dialog box (zenity). I'm using Linux Mint 13 Maya XFCE.
Here is my crontab setup
* * * * * /home/x/Documents/MyScripts/Cron/BeepAlarm "Wake Up"
And here is my script
!#/bin/bash
# Initialize Variables
Text=$1
Title="Alarm"
OkLabel="Snooze"
CancelLabel='Shut It Up!'
Icon=/home/x/.icons/actions/48/appointment-new.png # Won't work
SnoozeTimeout=120
AlarmCycles=100
shopt expand_aliases
alias vol='amixer -q -c 0 sset Beep'
vol 25% # Heart friendly
rm -f LoopMode # Just in case =P
# Dialog Box Function
_MsgBox () {
if zenity --question --title="$Title" \
--window-icon=$Icon --ok-label="$OkLabel" \
--cancel-label="$CancelLabel" --text="$Text"
then
echo 1 > LoopMode
else
echo 2 > LoopMode
#kill -TERM `jobs -p` # Won't work
fi
}
# Alarm Loop
while [ $AlarmCycles -gt 0 ]; do
case `cat LoopMode` in
"") # Question Box
echo 0 > LoopMode
_MsgBox &
;;
1) # Snooze
vol 25%
rm -f LoopMode
sleep $SnoozeTimeout
;;
2) # Stop Alarm
break # But don't sleep again
;;
*) # Get on my nerves
beep -r 4 -l 20 -f 2000; sleep 0.5
beep -r 4 -l 20 -f 4000; sleep 0.5
AlarmCycles=$((AlarmCycles-1))
vol 5%+
esac
done
# Housekeeping~
kill $(($!+2)) # I don't know a better way to kill zenity Dialog
vol 25% # =)
rm -f LoopMode
As Rob suggested:
* * * * * env DISPLAY=:0.0 /home/x/Documents/MyScripts/Cron/BeepAlarm "Wake Up"
P.S: Sorry, I didn't pay attention to the formalities
Related
I keep getting zero status even after interrupting the script.
The first script
#!/bin/bash
## call the backup script
/usr/local/bin/backup 2>&1 >/dev/null
echo $?
backup
#!/bin/bash
exitscript() {
rm -f $LOCKFILE
echo "Script Status: $1 | tee -a ${LOG}"
echo "> End Date: $(date +'%d.%m.%Y %H:%M:%S')" | tee -a ${LOG}
exit $1
}
######START#######
trap "exitscript 1" 1 2 23 24 25
rsync ${args} ${src} ${dest} | tee -a ${RSYNC_LOG}
retcode=${PIPESTATUS[0]}
if [[ ${retcode} -ne 0 ]]; then
exitcode=1
fi
exitscript ${exitcode:-0}
When the First Script is run, it returns exit status of 0 although i have tried to kill the backup script before it ends (for that i have created a very large size file so that rsync takes time to copy the file and i get the time to kill the script before it ends)
ps -ef | grep -i backup
kill $PID
Another thing is that even after killing the backup script, rsync still runs. I would like for rsync to stop once the script is being killed and my first script to return the status code of zero.
Much appreciation for any suggestions. Thanks!
I assume the missing quote in echo "Script Status: $1 | tee -a ${LOG} is not relevant to the question.
When you want a function to handle the trap, you need to export that function.
And when you want to kill children, you should add these in your trap-function.
I tested these adjustments with a sleep command, it should work for rsync too.
#!/bin/bash
exitscript() {
echo "Script Status: $1"
(( $pleasekill > 0 )) && kill ${pleasekill}
echo "> End Date: $(date +'%d.%m.%Y %H:%M:%S')"
exit $1
}
# Export the function exitscript
export exitscript
######START#######
pleasekill=0
trap "exitscript 1" 1 2 23 24 25
# Start I/O-friendly rsync function
sleep 30 &
pleasekill=$!
wait
exitscript 2
When you test this with the first script, use ^C or kill -1 pid_of_backup.
I am running a numbers of commands from a script and measuring the execution time (of only several of them). This I know how to do with time. But I also want to output all the times only after the whole script is finished (either in the shell or in a file). How do I do that?
EDIT:
I am sorry, I should have specified that I am using a Fish shell.(Nevertheless, I will add bash to the tags so that other people can use the answers.)
#!/bin/bash
#
declare -a toutput
declare -a commands
#
stime()
{
start=`date +%s`
# run command
$1
end=`date +%s`
toutput+=("$1 : $((end-start)) ,")
}
# set array of commnds
commands+=("'ls -1 /var/log'")
commands+=("'sleep 3'")
commands+=("'sleep 5'")
echo "==================="
echo ${commands[#]}
echo "==================="
# execute commands and log times to toutput
#
for cc in "${commands[#]}"
do
stime "$(echo ${cc} | tr -d \')"
done
echo "times = (" ${toutput[#]} ")"
Bash 4.2 and up have an obscure command for saving the unix time to a variable.
#!/bin/bash
# start time
printf -v s_time '%(%s)T' -1
# do stuff
sleep 1
sleep 2
sleep 3
# end time
printf -v e_time '%(%s)T' -1
# do more stuff
sleep 4
# print result
echo It took $(( e_time - s_time )) seconds
Shows the run time of the "do stuff" multiple commands
It took 6 seconds
option 1:
just try to run your script in this way:
time ./your_script.sh
https://www.cyberciti.biz/faq/unix-linux-time-command-examples-usage-syntax/
option 2:
npm install -g gnomon
./your_script.sh | gnomon
https://github.com/paypal/gnomon
I am working on a script where the user should provide his/her input within 20 seconds. If it's not provided within that time, then the script should exit
countdown()
(
# https://www.cyberciti.biz/faq/how-to-display-countdown-timer-in-bash-shell-script-running-on-linuxunix/
IFS=:
set -- $*
secs=$(( ${1#0} * 3600 + ${2#0} * 60 + ${3#0} ))
while [ $secs -gt 0 ]
do
sleep 1 &
printf "\r%02d:%02d:%02d" $((secs/3600)) $(( (secs/60)%60)) $((secs%60))
secs=$(( $secs - 1 ))
wait
done
echo
)
echo -n "Your answer : countdown "00:00:20" "; read readUserInput
where the user should provide his/her input within 20 seconds , if its not provided then the script should exit
So timeout the read after 20 seconds.
# the user should provide his/her input within 20 seconds
if ! read -r -t 20 input; then
# if its not provided then the script should exit
exit
fi
Note that -t option to read is a bash extension to posix shell. On posix compatible shell, I think I would go with timeout command and a subshell reading from user, something along input=$(timeout 20 sh -c 'read -r input && printf "%s\n" "$input"').
[Imaginary question OP never asked]: How to ask user for input with a 20 seconds timeout and at the same time provide a timer counting from 20 to 0?
Display a countdown in a background process. The following implementation uses SIGUSR1 to communicate with the background process that it's time to stop (well, using SIGTERM doesn't want to work well). Also it uses \e[s and \e[u ansi escape codes to restore cursor position after each inputted character to proper location. TLDR: looks like fun.
countdown() (
# https://www.cyberciti.biz/faq/how-to-display-countdown-timer-in-bash-shell-script-running-on-linuxunix/
local fmt=$1
local secs=$(($2+1))
trap exit SIGUSR1
printf "$fmt" "$(printf "%02d:%02d:%02d" "$((secs / 3600))" "$(( (secs / 60) % 60))" "$((secs % 60))")"
while ((secs--)); do
sleep 1 &
printf "\e[s\r$fmt\e[u" "$(printf "%02d:%02d:%02d" "$((secs / 3600))" "$(( (secs / 60) % 60))" "$((secs % 60))")"
wait
done
printf "\r%fmt" "00:00:00"
)
countdown_end() {
pkill -SIGUSR1 -P "$1" # sending SIGUSR1 hoping for gracefull exit
wait "$1"
}
timeout=5
countdown "Countdown %s: input: " "$timeout" &
child=$!
if ! read -r -t "$timeout" readUserInput; then
countdown_end "$child"
echo
exit
fi
countdown_end "$child"
echo "You inputted " "$readUserInput"
I want to synchronize between processes. My computer has two cores. User can enter the simulation number from command line. If input is greater than 2, the 3rd and other processes have to wait until one of the earlier processes is finished. If one of them is finished, the next process should be started. For example, say the first two processes are already running. The first process finishes before the 2nd. Now the 3rd process should be start.
I am new in bash, I figured out. It is seen that anywait: command not found. How can I do that? Here is my script:
#!/bin/bash
# My first script
count=2
echo -n "Please enter the number of simulation :"
read number
echo "Please enter the algorithm type "
printf "0 for NNA\n1 for SPA\n2 for EEEA :"
while read type; do
case $type in
0 ) cd /home/cea/Desktop/simulation/wsnfuture
taskset -c 0 ./wsnfuture -u Cmdenv omnetpp.ini > /home/cea/Desktop/simulation/RESULTS/NNA/NNA0/0 &
taskset -c 1 ./wsnfuture -u Cmdenv omnetpp.ini > /home/cea/Desktop/simulation/RESULTS/NNA/NNA0/1 &
while [ $count -lt $number ]; do
anywait
cd /home/cea/Desktop/simulation/wsnfuture
mkdir /home/cea/Desktop/simulation/RESULTS/NNA/NNA$count
taskset -c $((count % 2)) ./wsnfuture -u Cmdenv omnetpp.ini > /home/cea/Desktop/simulation/RESULTS/NNA/NNA$count/$count &
count=$((count + 1))
done
;;
1 ) while [ $count -lt $number ]; do
cd /home/cea/Desktop/simulation/wsnfuture1
taskset -c $((count % 2)) ./wsnfuture -u Cmdenv omnetpp.ini > /home/cea/Desktop/simulation/RESULTS/SPA/$count &
count=$((count + 1))
done
;;
2 ) while [ $count -lt $number ]; do
cd /home/cea/Desktop/simulation/wsnfuture2
taskset -c $((count % 2)) ./wsnfuture -u Cmdenv omnetpp.ini > /home/cea/Desktop/simulation/RESULTS/EEEA/$count &
count=$((count + 1))
done
;;
* ) echo "You did not enter a number"
echo "between 0 and 2."
echo "Please enter the algorithm type "
printf "0 for NNA\n1 for SPA\n2 for EEEA :"
esac
done
function anywait(){
while ps axg | grep -v grep | grep wsnfuture> /dev/null; do sleep 1; done
}
You can achieve a simple way of process synchronization in bash using wait which waits for one or more number of background jobs to complete before running the next.
You generally run jobs in the background by appending the & operator to the end of a command. At that point the PID (process ID) of the newly created background process is stored in a special bash variable: $! and wait command allows this process to be terminate before running the next instruction.
This can be demonstrated by a simple example
$ cat mywaitscript.sh
#!/bin/bash
sleep 3 &
wait $! # Can also be stored in a variable as pid=$!
# Waits until the process 'sleep 3' is completed. Here the wait on a single process is done by capturing its process id
echo "I am waking up"
sleep 4 &
sleep 5 &
wait # Without specifying the id, just 'wait' waits until all jobs started on the background is complete.
echo "I woke up again"
Command ouput
$ time ./mywaitscript.sh
I am waking up
I woke up again
real 0m8.012s
user 0m0.004s
sys 0m0.006s
You can see the script has taken ~8s to run to completion. The breakdown on the time is
sleep 3 will take full 3s to complete its execution
sleep 4 and sleep 5 are both started sequentially one after next and it has taken the max(4,5) which is approximately ~5s to run.
You can apply the similar logic to your question above. Hope this answers your question.
Your code has many other problems, but the answer is that you should declare anywait before using it (so moving it up in your script).
Please consider using http://www.shellcheck.net/ to at least suppress the most obvious errors/mistakes in your script.
im trying to start my serial port application inside a bash script. for some strange reason the application cannot fetch the /dev/ttyACM0 device file. if i type ls -la in /dev/ directly from a terminal the device file visible. When i try the same thing in a bash script every device file is visible except for the ttyACM0.
in terminal:
root#pc:~$ ls -la /dev/ttyAC*
crw-rw---T 1 root dialout 166, 0 Feb 18 17:25 /dev/ttyACM0
in script:
ls: cannot access /dev/ttyAC*: No such file or directory
All other device files are still visible.. Does someone have any idea how this is possible?
Here is the script:
#!/bin/bash
# gpio defs
mcu_reset=49 #gpio2_17
mcu_erase=50 #gpio2_18
dir_export="/sys/class/gpio/export"
dir_reset="/sys/class/gpio/gpio$mcu_reset"
dir_erase="/sys/class/gpio/gpio$mcu_erase"
# Initialise Reset and erase gpio
if [ ! -d "$dir_reset" ]; then
echo $mcu_reset > $dir_export
echo out > "$dir_reset/direction"
echo $mcu_erase > $dir_export
echo out > "$dir_erase/direction"
echo 0 > "$dir_erase/value"
echo 1 > "$dir_reset/value"
echo "* GPIO directories initialised!"
fi
# erase mcu flash
echo "* Erasing device..."
echo 1 > "$dir_erase/value"
sleep 0.5
echo 0 > "$dir_erase/value"
sleep 0.5
# reset device
echo "* Resetting device..."
echo 0 > "$dir_reset/value"
sleep 0.5
echo 1 > "$dir_reset/value"
sleep 0.5
# Flash device
#$(bossac -e -d -w -v -b argv[0])
ls -la /dev/ttyAC*
# reset device
echo "* Resetting device..."
echo 0 > "$dir_reset/value"
sleep 0.5
echo 1 > "$dir_reset/value"
sleep 0.5
Found the problem! When 'echo 1 > "$dir_reset/value" gets executed the mcu connected to linux resets. the sleep of 0.5 seconds was to short for the mcu to come up again.