Nesting for loop inside a while loop on one line - nested-loops

I am trying to run a script looping forever every ten seconds five times one second apart. How can I do this from command line instead of a script?
This does not work:
while true; do; sleep 10 && for i in `seq 3` do; sleep 1 && date; done; done
This works in a script:
#!/bin/ash
while true; do
sleep 10
for i in `seq 3`; do
sleep 1 && date
done
done
If it's relevant this is to blink an led in a specific pattern on raspberry pi not to print the date the date command is just to see whats happening.

Try this:
while true; do sleep 10 ; for i in `seq 3`; do sleep 1 && date ; done ; done
No semi column should be used after do keyword. No need to use && before starting the for loop in your case. Using && before for loop mean, execute the loop only if sleep 10 command is success.
In your case, though you use && before for loop or not, it gives the behavior would be same.

Related

I am using `xprintidle` to trigger a bash script after X seconds of inactivity. How do I make it loop?

I am using xprintidle to trigger a bash script after X seconds of inactivity.
#!/bin/bash
idletime=$((3*1000)) # 3 seconds in milliseconds
# For testing purposes, I am
# using 3 seconds.
idle=0
while [ $idle -lt $idletime ];do
idle=`xprintidle`
sleep 1
done
/my/bash/script/
The script above works, but it only runs once. How to I cause the script to loop?
enclose with a while true so it runs forever:
while true; do
idle=0
while [ $idle -lt $idletime ];do
idle=`xprintidle`
sleep 1
done
/my/bash/script
done
you might write it more succinctly:
while true; do (($(xprintidle) >= 3000)) && myscript; sleep 1; done
but, same thing

Getting the output from first command to exit in Bash

Suppose I have a Bash script that takes one parameter (ie. port number) and performs some logic with either outcome
hangs for 1 second and exits with timeout error, or
succeeds within milliseconds and outputs a message such as "ON" or "OFF".
One way to find the parameter that results in outcome 2 would be run the script over loop on the possible values of the parameter, but this would take N seconds to find the parameter. Assume the output is consistent for simplicity (ie. if more than one instance results in outcome 2, their output message is the same).
Is there a way to run the N instances of the Bash script at the same time (within reasonable milliseconds) with each possible value of the parameter, and get the output message from the instance that finished fastest? This would result in getting the output in under 1 second.
To allow a reproducer, let's first create a function that behaves akin to your command:
try_or_wait() {
local argument=$1 # doesn't really matter for our purposes
if (( RANDOM % 10 == 0 )); then # 10% return a result immediately
if (( (RANDOM + argument) % 2 == 0 )); then
echo ON
else
echo OFF
fi
else # 90% hang, and then fail
sleep 10
exit 1
fi
}
...so, how can we use this?
declare -a pids=( )
mkfifo out.fifo
# start 20 processes, passing your port number to each
for ((i=0; i<20; ++i)); do
try_or_wait "$1" >out.fifo & pids+=( "$!" )
done
# read one result
read result <out.fifo
# kill all the remaining children
kill "${pids[#]}"
echo "Got the result: $result"
Not that this is the only approach. To have GNU xargs do the work of spawning subprocesses for you:
export -f try_or_wait # export shell functions so child processes can run it
read -r result < <(
for ((i=0; i<20; ++i)); do
printf '%s\0' "$1"
done | xargs -0 -n 1 -P 20 bash -c 'try_or_wait "$#"' _
)
Note that the above is safe because each instance does its writes all at once -- a single echo, short enough for the entire output to fit into a single syscall. If each instance of your program may have enough output to require multiple syscalls (or output split over multiple writes), you may require a tool that can perform collation for you instead.
With GNU Parallel it looks like this:
try_or_wait() {
local argument=$1 # doesn't really matter for our purposes
if (( RANDOM % 10 == 0 )); then # 10% return a result immediately
if (( (RANDOM + argument) % 2 == 0 )); then
echo ON
else
echo OFF
fi
else # 90% hang, and then fail
# Example showing child process having further child processes
# These should all be killed if try_or_wait is killed
sleep 97 &
( ( (sleep 100 &); sleep 99 &); sleep 98 &)
sleep 10
exit 1
fi
}
export -f try_or_wait
seq 100 | parallel -j0 --halt now,done=1 try_or_wait
It deals correctly by killing the grand children of try_or_wait and will also act correctly if the output of try_or_wait outputs more data than what fits in a single syscall (For more than you ever wanted to learn on this issue see: https://catern.com/posts/pipes.html).

Shell Script to run command every 5secs for x number of secs

I'm learning bash scripting and I want to call a function every 5 seconds for x number of seconds. The x is determined by the command line argument when the script was run. I've tried watch but it seems to go on forever, I don't seem to have an ability to return after x seconds. There might be a sleep way but it seems clumsy and I have to deal with local drift. Is there an elegant solution?
My code:
#!/bin/bash
if [ $# -ne 1 ]; then # only 1 command line arg allowed
echo "Incorrect arguments"
exit 1
elif ! [[ $1 =~ ^[0-9]+$ ]]; then # arg must be digit
echo "Argument must be a positive number"
exit 1
fi
ask()
{
OUTPUT=$(snmpwalk -v2c -c community server oib)
CLEANOUTPUT="${OUTPUT:32}%"
echo $CLEANOUTPUT
}
#export -f ask
#watch -n5 ask
The SECONDS variable counts the number of seconds since bash was started:
#!/bin/bash
while (( SECONDS <= 20 ))
do
echo "Running something"
sleep 5 || break
done
echo "Done"
This simplistic method will sleep 5 after the last run even when you know it'll go over 20 seconds, and it does not try to account for the run time of the command (if the command runs for 2 seconds, it'll end up starting up once every 7 seconds).
If you want to be a little bit more accurate with the timing, you can put a sleep into the background, do whatever work is required, then use wait, which will halt foreground processing until the sleep exits. While accuracy won't be perfect, at least the time taken by the work part is not included.
Something like:
#!/bin/bash
LOOPTIMES=3
REPEATEVERY=5 # seconds
for (( count=0 ; $count < $LOOPTIMES ; count++ ))
do
/bin/sleep $REPEATEVERY &
echo "Doing work $count..."
# do whatever
wait # this will wait until the sleep exits
done
exit 0
#!/bin/bash
if [ $# -ne 1 ]; then # only 1 command line arg allowed
echo "Incorrect arguments"
exit 1
elif ! [[ $1 =~ ^[0-9]+$ ]]; then # arg must be digit
echo "Argument must be a positive number"
exit 1
fi
iterations=$(($1/5))
for i in $(seq 1 $iterations);
do
OUTPUT=$(snmpwalk -v2c -c community server oib)
CLEANOUTPUT="${OUTPUT:32}%"
echo $CLEANOUTPUT
sleep 5
done

How to set the timer in shell

Can we set the timer in shell script?
means if I set the timer for 20 secs my shell scripts should be execute like this :
20..19..18..17..
after completing the I should get the result. Is it possible ? Please assist me.
It's not entirely clear what you want: if xyz.sh finishes in 3 seconds, do you want to wait an additional 17 seconds? Or do you want to interrupt xyz.sh after 20 seconds and make it produce output? If the latter:
$ cat a.sh
#!/bin/bash
./xyz.sh &
i=${1-20}
echo
while test $i -gt 0; do printf "\r$((i--))..."; sleep 1; done
kill $!
$ cat xyz.sh
#!/bin/sh
foo() { echo "this program doesn't do much"; }
trap foo TERM
sleep 30 &
wait
With for loop it looks like:
#!/bin/bash
for (( i=20; i>0; i-- ))
do
clear
echo -n "$i"
sleep 1
done

Syntax for a single-line while loop in Bash

I am having trouble coming up with the right combination of semicolons and/or braces. I'd like to do this, but as a one-liner from the command line:
while [ 1 ]
do
foo
sleep 2
done
while true; do foo; sleep 2; done
By the way, if you type it as a multiline (as you are showing) at the command prompt and then call the history with arrow up, you will get it on a single line, correctly punctuated.
$ while true
> do
> echo "hello"
> sleep 2
> done
hello
hello
hello
^C
$ <arrow up> while true; do echo "hello"; sleep 2; done
It's also possible to use sleep command in while's condition. Making one-liner looking more clean imho.
while sleep 2; do echo thinking; done
Colon is always "true":
while :; do foo; sleep 2; done
You can use semicolons to separate statements:
$ while [ 1 ]; do foo; sleep 2; done
You can also make use of until command:
until ((0)); do foo; sleep 2; done
Note that in contrast to while, until would execute the commands inside the loop as long as the test condition has an exit status which is not zero.
Using a while loop:
while read i; do foo; sleep 2; done < /dev/urandom
Using a for loop:
for ((;;)); do foo; sleep 2; done
Another way using until:
until [ ]; do foo; sleep 2; done
Using while:
while true; do echo 'while'; sleep 2s; done
Using for Loop:
for ((;;)); do echo 'forloop'; sleep 2; done
Using Recursion, (a little bit different than above, keyboard interrupt won't stop it)
list(){ echo 'recursion'; sleep 2; list; } && list;
A very simple infinite loop.. :)
while true ; do continue ; done
Fr your question it would be:
while true; do foo ; sleep 2 ; done
For simple process watching use watch instead
I like to use the semicolons only for the WHILE statement,
and the && operator to make the loop do more than one thing...
So I always do it like this
while true ; do echo Launching Spaceship into orbit && sleep 5s && /usr/bin/launch-mechanism && echo Launching in T-5 && sleep 1s && echo T-4 && sleep 1s && echo T-3 && sleep 1s && echo T-2 && sleep 1s && echo T-1 && sleep 1s && echo liftoff ; done
If you want the while loop to stop after some condition, and your foo command returns non-zero when this condition is met then you can get the loop to break like this:
while foo; do echo 'sleeping...'; sleep 5; done;
For example, if the foo command is deleting things in batches, and it returns 1 when there is nothing left to delete.
This works well if you have a custom script that needs to run a command many times until some condition. You write the script to exit with 1 when the condition is met and exit with 0 when it should be run again.
For example, say you have a python script batch_update.py which updates 100 rows in a database and returns 0 if there are more to update and 1 if there are no more. The the following command will allow you to update rows 100 at a time with sleeping for 5 seconds between updates:
while batch_update.py; do echo 'sleeping...'; sleep 5; done;
You don't even need to use do and done. For infinite loops I find it more readable to use for with curly brackets. For example:
for ((;;)) { date ; sleep 1 ; }
This works in bash and zsh. Doesn't work in sh.
If I can give two practical examples (with a bit of "emotion").
This writes the name of all files ended with ".jpg" in the folder "img":
for f in *; do if [ "${f#*.}" == 'jpg' ]; then echo $f; fi; done
This deletes them:
for f in *; do if [ "${f#*.}" == 'jpg' ]; then rm -r $f; fi; done
Just trying to contribute.
You can try this too
WARNING: this you should not do but since the question is asking for infinite loop with no end...this is how you could do it.
while [[ 0 -ne 1 ]]; do echo "it's looping"; sleep 2; done
You can also put that loop in the background (e.g. when you need to disconnect from a remote machine)
nohup bash -c "while true; do aws s3 sync xml s3://bucket-name/xml --profile=s3-profile-name; sleep 3600; done &"

Resources