while [condition]
do
for [condition]
do
if [ "$x" > 3 ];then
break
fi
done
if [ "$x" > 3 ];then
continue
fi
done
In the above script I have to test "$x" > 3 twice. Actually the first time I test it, if it is true I want to escape the while loop and continue to the next while loop.
Is there any simpler way so I can use something like continue 2 to escape the outer loop?
"break" and "continue" are close relatives of "goto" and should generally be avoided as they introduce some nameless condition that causes a leap in the control flow of your program. If a condition exists that makes it necessary to jump to some other part of your program, the next person to read it will thank you for giving that condition a name so they don't have to figure it out!
In your case, your script can be written more succinctly as:
dataInRange=1
while [condition -a $dataInRange]
do
for [condition -a $dataInRange]
do
if [ "$x" > 3 ];then
dataInRange=0
fi
done
done
Related
I have a Bash script where I want to count how many things were done when looping through a file. The count seems to work within the loop but after it the variable seems reset.
nKeys=0
cat afile | while read -r line
do
#...do stuff
let nKeys=nKeys+1
# this will print 1,2,..., etc as expected
echo Done entry $nKeys
done
# PROBLEM: this always prints "... 0 keys"
echo Finished writing $destFile, $nKeys keys
The output of the above is something alone the lines of:
Done entry 1
Done entry 2
Finished writing /blah, 0 keys
The output I want is:
Done entry 1
Done entry 2
Finished writing /blah, 2 keys
I am not quite sure why nKeys is 0 after the loop :( I assume it's something basic but damned if I can spot it despite looking at http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-7.html and other resources.
Fingers crossed someone else can look at it and go "well duh! You have to ..."!
In the just-released Bash 4.2, you can do this to prevent creating a subshell:
shopt -s lastpipe
Also, as you'll probably see at the link Ignacio provided, you have a Useless Use of cat.
while read -r line
do
...
done < afile
As mentioned in the accepted answer, this happens because pipes spawn separate subprocesses. To avoid this, command grouping has been the best option for me. That is, doing everything after the pipe in a subshell.
nKeys=0
cat afile |
{
while read -r line
do
#...do stuff
let nKeys=nKeys+1
# this will print 1,2,..., etc as expected
echo Done entry $nKeys
done
# PROBLEM: this always prints "... 0 keys"
echo Finished writing $destFile, $nKeys keys
}
Now it will report the value of $nKeys "correctly" (i.e. what you wish).
I arrived at the desired result in the following way without using pipes or here documents
#!/bin/sh
counter=0
string="apple orange mango egg indian"
str_len=${#string}
while [ $str_len -ne 0 ]
do
c=${string:0:1}
if [[ "$c" = [aeiou] ]]
then
echo -n "vowel : "
echo "- $c"
counter=$(( $counter + 1 ))
fi
string=${string:1}
str_len=${#string}
done
printf "The number of vowels in the given string are : %s "$counter
echo
this script tries to ping through a hosts string array.
for i in "${arr[#]}"
do
echo "check if $i is online"
ping -c1 $i &>/dev/null
if[ $? -eq 0 ] then
echo "$i is online"
else
echo "$i is not online"
fi
done
I am new to bash scripting so if some one could tell me why I get an
syntax error near unexpected token `else'
and also if this is a good approach. Thanks in advance
[ is not part of bash's syntax; it is a regular character like a or 8. As such, the parser does not recognize if[ as two words if and [; it just sees the name if[, which it assumes might be a command name followed by a series of arguments terminated by the end of the line. The following line is also a valid command. The next line, though, begins with else, which is a recognized keyword and thus cannot occur in command position, but only as part of an on-going if statement, triggering the error.
All of which is to say, you must separate if and [ with a space.
if [ $? -eq 0 ]; then
(You also need a semi-colon before then if it appears on the same line, which would be the next problem you encountered after fixing the space issue.)
The line number 6, should have been with a semi-colon ; and need a space after the if in if[ $? of your line.
if [ $? -eq 0 ]; then
More recommended way is you can directly use ping's exit-code in if-statement as
if ping -c 1 "$i" &> /dev/null
then
echo "$i is online"
else
echo "$i is not online"
fi
See the below excerpt from the man ping page on why I used the -c flag.
-c count
Stop after sending count ECHO_REQUEST packets. With deadline option, ping waits for count ECHO_REPLY packets, until the timeout expires.
Use http://www.shellcheck.net/ , to debug such trivial syntax errors.
if [ $? -eq 0 ]; then You need to have a space between the if and the [ and also you missed a ;
I want to increase the var upPCs by one every time I go through the if statement.
upPCs= 0;
if [ $? -eq 0 ]; then
$upPCs ++;
fi
This doesn't work.
Bash (and other shells) do not allow spaces around the equals sign. A semi-colon ; is used to separate commands on a line, so putting one at the end of the line is a waste (unless your really enjoy typing semi-colons...).
upPCs=0
Use (( )) for arithmetics:
if (( $? == 0 )); then
(( upPCs++ ))
fi
Finally, you should indent your code inside if (and while, for, etc) statements, because if you don't a kitten dies. Seriously, scripts can get unreadable quite quickly if you do not indent - it is worth it.
By the way, often you don't need to test $? explicitly. You don't show the command that you are testing the result of, but the principle is that if is true if the command is successful (exit code of zero is considered success). For example:
if some_command
then
(( upPCs++ ))
fi
is it possible to assign variable inside if conditional in bash 4? ie. in the function below I want to assign output of executing cmd to output and check whether it is an empty string - both inside test conditional. The function should output
"command returned: bar"
myfunc() {
local cmd="echo bar"
local output=
while [[ -z output=`$cmd` ]];
do
#cmd is failing so far, wait and try again
sleep 5
done
# great success
echo "command returned: $output"
}
why the above?
i prefer to run scripts with 'set -e' - which will cause script to terminate on first non-0 return/exit code that's not in an if/loop conditional.
with that in mind, imagine cmd is an unstable command that may exit with > 1 from time to time, and I want to keep calling it until it succeeds and i get some output.
You can try something like this:
myfunc() {
local cmd="echo bar"
local output=
while ! output=$($cmd) || [[ -z output ]];
do
#cmd is failing so far, wait and try again
sleep 5
done
# great success
echo "command returned: $output"
}
Note that it is strongly recommended to avoid the use of set -e.
I don't think you would be able to do it in your conditional
As yi_H pointed out, the if is equivalent to
if [[ ! -z output=bar ]];
which in turn is basically
if [[ ! -z "output=bar" ]];
So, all you are checking is if the string "output=bar" is empty or not...
So, output=bar could actually be anything like !##!#%=== and it would still do the same thing (that is, the expression isn't evaluated). You might have to assign the variable in a subshell somehow, but I'm not sure that would work.
Since assignment won't work there, you need some workaroudn.
You could temporary do a set +e...
You could use this way ...
$cmd
exit_status=$?
while [[ $exit_status -gt 0 ]];
do
#cmd is failing so far, wait and try again
sleep 5
$cmd
exit_status=$?
done
EDIT: This won't work with 'set -e' or other way around, don't use 'set -e' to begin with.
I'm a C/C++ programmer and quite stupid in general (or at least the way bash does things it makes me feel confused). I can't wrap my head around process substitution.
I need to define a global boolean, set it somewhere in a loop, and make use of it in global scope. Could someone please explain in the simplest way possible how to adapt the code below to allow me to achieve my use case, simple enough so that I don't have to contort my brain again tomorrow to try and grasp process substitution .
# DEFINE HERE
for i in `seq 0 ${DAEMON_COUNT}`;
do
if [ ! -d "data$i" ]; then
# SET HERE
echo "data$i does not exist. Creating...";
mkdir data$i
fi
done
# TEST AND USE HERE
to be honest, I don't think bash is up to the task.... the next block looks like this.
echo "-------------------------------------------------------------------------------"
echo "checking the state of potentially running daemons"
for i in `seq 0 ${DAEMON_COUNT}`;
do
if [ ! -e "data$i/mongod.lock" ] ; then
echo "[no lock file] mongod process $i does not exist"
else
echo "[lock file exists] process $i lock file exists "
I_PID=`cat data$i/mongod.lock`
if [ ! ${I_PID} ]; then
echo " [GOOD] lock pid empty"
elif [ "`ps -p ${I_PID} | grep ${I_PID}`" ]; then
echo " [GOOD] data1 pid: ${I_PID} running"
else
echo "[PROBABLY FATAL] data1 pid: ${I_PID} not running."
fi
fi
done
echo "-------------------------------------------------------------------------------"
What I now need is a global array of structs so that I can loop over them and take conditional action to initialize my daemons correctly :/.
Might just use libc and do this stuff in lua, the only reason I hold back is having to install rocks, I don't like ad-hoc code repositories vomiting whatever they want onto my machine :D
The important thing to understand is this: child process is born with its own environment and cannot affect the variables of its parent. If you set a variable in a child process, then the value of the variable in the parent is not affected. These are actually two different variables which just happen to have the same name.
The second thing to understand is when bash runs a command as a child process. There are two cases relevant to the question:
Each process connected by a pipe | is a child of the current shell.
Running a single builtin command with a redirection (e.g. <) will not spawn a child process.
Here is a simple session which demonstrates these ideas:
$ somevar=initial
$ echo test1 | read somevar
$ echo $somevar
initial
$ read somevar < <(echo test2)
$ echo $somevar
test2
The first read is a child process and therefore somevar in the main shell does not change. The second read is executed by the main shell itself and hence somevar is updated.
This means that your code will work as you expect unless you add a pipe in front of or after the for loop, i.e. this works as you want it to:
# DEFINE HERE
SOMEVAR=0
DAEMON_COUNT=10
for i in `seq 0 ${DAEMON_COUNT}`;
do
if [ ! -d "data$i" ]; then
# SET HERE
SOMEVAR=10
echo "data$i does not exist. Creating...";
mkdir data$i
fi
done
# TEST AND USE HERE
echo ${SOMEVAR} # This displays 10
I might have misundestood but...
bool=false;
for i in `seq 0 ${DAEMON_COUNT}`;
do
if [ ! -d "data$i" ]; then
bool=true;
echo "data$i does not exist. Creating...";
mkdir data$i
fi
done
if [ $bool = true ]; then
...
fi
Is this what you want?