I'm trying to make a call using a call file on asterisk where it plays a file and hangs up after a given time whether the sound file has finished or not.
I have it working with this bash script as the AGI script:
#!/bin/bash
Duration="30"
file="sound.mp3"
while read VAR && [ -n ${VAR} ] ; do : ; done
echo "ANSWER"
read RESPONSE
echo 'SET AUTOHANGUP $Duration'
echo 'EXEC PLAYBACK "'$file'" ""'
read RESPONSE
exit 0
The problem is that the asterisk cdr logs show the call to last 30 seconds whether the the person on the other end has hung up or not...
Can anyone help?
OK this is embarrassing...
I cleaned up my code so that I could paste it on here and when I try again it seems to work how I want. Still don't know what then problem was but this code seems to work
#!/bin/bash
Duration="20"
file="soundfile"
while read VAR && [ -n ${VAR} ] ; do : ; done
echo "ANSWER"
read RESPONSE
echo 'SET AUTOHANGUP '$Duration''
echo 'EXEC PLAYBACK "'$file'" ""'
read RESPONSE
exit 0
You can use the signal facility. For example:
#!/bin/bash
times_up(){ echo "time is up, pens down."; exit 0; }
trap 'times_up' ALRM
(sleep 10;kill -0 $$ && kill -ALRM $$)&
s=0
while (true) do
sleep 1
echo "$s: do my stuff"
let s=$s+1
done
Related
I wanted to do a shell function which countdown to n seconds (10 for exemple) and after that, continue the script.
I tried with the sleep function but it does stop the script entirely.
I want something like when the user input "y" during this countdown, it will stop the countdown and do something particular (much like an "interrupt").
And if the countdown finishes without any user input, the script continues.
Thank you !
**UPDATE* *
#Krzysztof Księżyk That's exactly what i wanted !
One difference, if i want the read return only if "Y" is the input how can i do that ? i already tried with the -d and -a...
Here is my code :
label="NTFS"
read -n 1 -t 5 MS
if [ -z "$MS" ]
then
echo "You don't input value, default will be taken"
else
echo -e "\nYou pressed 'Y' and want change default backup device."
read -p "Please input label of your secondary backup device: " secondary_label
label=$secondary_label
fi
echo "the choosen backup device label is $label"
You can use read command, eg.
read -n 1 -t 10
It will wait up to 10 second just for 1 character.
** UPDATE **
modified solution based on extra info from author
CNT=0
while [ $CNT -lt 10 ]; do
(( CNT++ ))
read -s -n 1 -t 1 -r KEY
if [ "$KEY" == 'y' ]; then
echo 'doing something'
else
echo 'doing nothing'
fi
done
If you want an interrupt, you probably want to trap SIGINT:
#!/bin/sh
trap : SIGINT
echo begin
for((i=0;i<10;i++)); do printf "$i\r"; sleep 1; done &
wait || kill $!
echo
echo end
The script counts to 10, but the timer aborts when the user sends a SIGINT (eg ctrl-C)
How to check whether a long time task running properly? (How to launch a function after given time while a command is running)?
I'm writing a bash script to download some files regularly. I'd like to informed while a successful download is started.
But I couldn't make it right.
#!/bin/bash
URL="http://testurl"
FILENAME="/tmp/test"
function is_downloading() {
sleep 11
echo -e "$DOWNLOADING" # 0 wanted here with a failed download but always get empty
if [[ $DOWNLOADING -eq 1 ]]; then
echo "Send Message"
# send_msg
fi
}
while [[ 0 ]]; do
is_downloading &
DOWNLOADING=1
curl --connect-timeout 10 --speed-time 10 --speed-limit 1 --location -o "$FILENAME" "$URL"
DOWNLOADING=0
echo -e "$DOWNLOADING"
sleep 3600
done
is_downloading is running in another process, the best it could see is a copy of our variables at the time it started. Variables are not shared, bash does not support multi-threading (yet).
So you need to arrange some form of Inter-Process Communication (IPC). There are many methods available, I favour a named pipe (also known as a FIFO). Something like this:
function is_downloading() {
thepipe="$1"
while :
do
read -r DOWNLOADING < "$thepipe"
echo "$DOWNLOADING"
if [[ $DOWNLOADING -eq 1 ]]; then
echo "Send Message"
# send_msg
fi
done
}
pipename="/tmp/$0$$"
mkfifo "$pipename"
is_downloading "$pipename" &
trap 'kill %1;rm "$pipename"' INT TERM EXIT
while :
do
DOWNLOADING=1
echo "$DOWNLOADING" > "$pipename"
curl --connect-timeout 10 --speed-time 10 --speed-limit 1 --location -o "$FILENAME" "$URL"
DOWNLOADING=0
echo "$DOWNLOADING" > "$pipename"
sleep 3600
done
Modifications: taken the function call out of the loop. Tidy-up code put into a trap statement.
This question already has answers here:
Timeout a command in bash without unnecessary delay
(24 answers)
Closed 8 years ago.
I want to make my script wait for n seconds, during which user can abort the script. My code till now looks like this
echo "Start Date provided :" $STARTDATE
echo "End date provided :" $ENDDATE
echo ""
echo "Please check the dates provided and in case of error press CTRL+C"
seq 1 15 |while read i; do echo -ne "\rWaiting for $i seconds"; sleep 1; done
echo ""
echo "Executing query on the remote side, please wait..."
When the user presses ctrl+c though what happens is that the while loop ends and continues executing the rest of the script.
How can I make it abort the entire script? Thanks in advance for any advise
I would use trap
#!/bin/bash
trap 'echo -e "\nBye"; exit 1' INT
echo "Please check the dates provided and in case of error press CTRL+C"
seq 1 15 |while read i; do echo -ne "\rWaiting for $i seconds"; sleep 1; done
echo "Executing query on the remote side, please wait..."
Here's a simpler program that reproduces your problem:
true | sleep 10 # Hit Ctrl-C here
echo "why does this execute?"
The problem is that bash determines whether or not to continue the script based on whether the SIGINT was handled. It determines whether the SIGINT was handled based on whether or not the process it's currently running is killed by this signal.
Since seq prints its numbers and exits immediately and successfully, it's not killed by SIGINT. bash therefore incorrectly assumes the signal was handled, and it keeps going.
You can solve this in two ways:
Add a SIGINT handler that exits your script regardless of whether the signal appears to be handled.
Don't use seq or anything else that immediately exits.
For the first approach, see user3620917's answer.
The second approach,
for i in {15..1}
do
printf '\rYou have %d seconds to hit Ctrl-C' "$i"
sleep 1
done
To catch CTRL+C you can use trap. For example:
#!/bin/bash
trap 'got_one' 2
got_one() {
echo "I got one"
exit 69
}
echo -e "PID: $$, PPID: $PPID"
sleep 100
So your script can look like this:
#!/bin/bash
trap "interrupt" SIGINT
interrupt() {
echo -e "\nExecution canceled."
exit 69
}
countdown() {
duration=$1 # in seconds
seq $duration -1 0|while read i; do echo -ne "\rWaiting for $i seconds"; sleep 1; done
}
STARTDATE="foo"
ENDDATE="bar"
cat <<END
Start Date provided: $STARTDATE
End date provided: $ENDDATE
Please check the dates provided and in case of error press CTRL+C
END
countdown 15
echo -e "\nExecuting query on the remote side, please wait..."
I'm currently writing a bash script to do tasks automatically. In my script I want it to display progress message when it is doing a task.
For example:
user#ubuntu:~$ Configure something
->
Configure something .
->
Configure something ..
->
Configure something ...
->
Configure something ... done
All the progress message should appear in the same line.
Below is my workaround so far:
echo -n "Configure something "
exec "configure something 2>&1 /dev/null"
//pseudo code for progress message
echo -n "." and sleep 1 if the previous exec of configure something not done
echo " done" if exec of the command finished successfully
echo " failed" otherwise
Will exec wait for the command to finish and then continue with the script lines later?
If so, then how can I echo message at the same time the exec of configure something is taking place?
How do I know when exec finishes the previous command and return true? use $? ?
Just to put the editorial hat on, what if something goes wrong? How are you, or a user of your script going to know what went wrong? This is probably not the answer you're looking for but having your script just execute each build step individually may turn out to be better overall, especially for troubleshooting. Why not define a function to validate your build steps:
function validateCmd()
{
CODE=$1
COMMAND=$2
MODULE=$3
if [ ${CODE} -ne 0 ]; then
echo "ERROR Executing Command: \"${COMMAND}\" in Module: ${MODULE}"
echo "Exiting."
exit 1;
fi
}
./configure
validateCmd $? "./configure" "Configuration of something"
Anyways, yes as you probably noticed above, use $? to determine what the result of the last command was. For example:
rm -rf ${TMP_DIR}
if [ $? -ne 0 ]; then
echo "ERROR Removing directory: ${TMP_DIR}"
exit 1;
fi
To answer your first question, you can use:
echo -ne "\b"
To delete a character on the same line. So to count to ten on one line, you can do something like:
for i in $(seq -w 1 10); do
echo -en "\b\b${i}"
sleep .25
done
echo
The trick with that is you'll have to know how much to delete, but I'm sure you can figure that out.
You cannot call exec like that; exec never returns, and the lines after an exec will not execute. The standard way to print progress updates on a single line is to simply use \r instead of \n at the end of each line. For example:
#!/bin/bash
i=0
sleep 5 & # Start some command
pid=$! # Save the pid of the command
while sleep 1; do # Produce progress reports
printf '\rcontinuing in %d seconds...' $(( 5 - ++i ))
test $i -eq 5 && break
done
if wait $pid; then echo done; else echo failed; fi
Here's another example:
#!/bin/bash
execute() {
eval "$#" & # Execute the command
pid=$!
# Invoke a shell to print status. If you just invoke
# the while loop directly, killing it will generate a
# notification. By trapping SIGTERM, we suppress the notice.
sh -c 'trap exit SIGTERM
while printf "\r%3d:%s..." $((++i)) "$*"; do sleep 1
done' 0 "$#" &
last_report=$!
if wait $pid; then echo done; else echo failed; fi
kill $last_report
}
execute sleep 3
execute sleep 2 \| false # Execute a command that will fail
execute sleep 1
I've been trying to send some AT commands to my modem and want to capture response into a variable. Here's my code:
exec 3<>/dev/ttyUSB3
echo -e "AT+CGSN\n" >&3
cat <&3
#read -r RESPONSE <&3
#echo "Response was $RESPONSE"
exec 3<&-
exec 3>&-
Results:
$ ./imei_checker.sh
AT+CGSN
356538041935676
OK
AT+CGSN
356538041935676
OK
But if I change cat to read, it doesn't work:
$ ./imei_checker.sh
Response was AT+CGSN
2 more questions:
Why it show the dupplicate output?
How do I close the file handle properly? exec 3<&- and exec 3>&-
seems doesn't work. I have to press Ctrl+C to
get control of the Terminal back.
read will only read a single line, unlike cat which will basically read and echo until end of file.
For a read version, you're best off reading with a timeout up until the point you get the OK (and storing any line that contains a lot of digits).
I think you'll find that it's not the closing of the number 3 file handle that's stopping things - it's more likely to be the cat which will continue to read/echo until an end-of-file event that isn't happening.
You can be certain of this if you just put:
echo XYZZY
immediately before the closing exec statements. If it's still in the cat, you'll never see it.
So, using a looping read version will probably fix that as well.
By way of example, here's how you can use read to do this with standard input:
#!/bin/bash
NUM=
while true ; do
read -p "> " -t 10 -r RESP <&0
if [[ $? -ge 128 ]] ; then RESP=OK ; fi
echo "Entered: $RESP"
if [[ $RESP = OK ]] ; then break ; fi
if [[ $RESP =~ ^[0-9] ]] ; then NUM=$RESP ; fi
done
echo "Finished, numerics were: '$NUM'"
It uses the timeout feature of read to detect if there's no more input (setting the input to OK so as to force loop exit). If you do get an OK before then, it exits normally anyway, the timeout simply caters for the possibility that the modem doesn't answer as expected.
The number is set initially to nothing but overwritten by any line from the "modem" that starts with a number.
Two sample runs, with and without an OK response from the "modem":
pax> ./testprog.sh
> hello
Entered: hello
> 12345
Entered: 12345
> OK
Entered: OK
Finished, numerics were: '12345'
pax> ./testprog.sh
> hello
Entered: hello
> now we wait 10 secs
Entered: now we wait 10 secs
> Entered: OK
Finished, numerics were: ''
It wouldn't be too hard to convert that to something similar with you modem device (either read <&3 or read -u3 will work just fine).
That would basically translate to your environment as:
exec 3<>/dev/ttyUSB3
echo -e "AT+CGSN\n" >&3
NUM=
while true ; do
read -t 10 -r RESP <&3
if [[ $? -ge 128 ]] ; then RESP=OK ; fi
echo "Entered: $RESP"
if [[ $RESP = OK ]] ; then break ; fi
if [[ $RESP =~ ^[0-9] ]] ; then NUM=$RESP ; fi
done
echo "Finished, numerics were: '$NUM'"
exec 3<&-
exec 3>&-
Now I haven't tested that since I don't have a modem hooked up (been on broadband for quite a while now) but it should be close to what you need, if not exact.
read takes the descriptor to read from as the argument following -u. See help read for details.
If you want to get the individual lines into variables I'd suggest to wrap read into a while:
while read -r RESPONSE <&3; do
echo "Response was $RESPONSE"
## e.g.:
[ "$RESPONSE" = "OK" ] && break
done
However, if you want "everything" that is sent back to you to reside in $RESPONSE you could do it like this:
RESPONSE="$(cat <&3)"