Problem in Makefile forloop break statement - shell

print:
#for number in 10 11 12 13 14 15; do \
( echo "Number: $$number" ); \
( break ); \
( echo Break not working ); \
done
The output that i am getting
Number: 10
Break not working
Number: 11
Break not working
Number: 12
Break not working
Number: 13
Break not working
Number: 14
Break not working
Number: 15
Break not working
The output that i need :
Number: 10

In the posted code the 'for' loop is executed in the shell (bash ?), not by the 'make' utility.
The problem with the bash script is that the 'break' statement is running in a sub-process - as it is placed inside parenthesis '( break '). As per bash manual, control-flow commands (while, if, for, return, ...) should executed in the "main" process. It is not possible to split them across processes.
Removing the '(' should solve the problem - the loop will stop after printing 'Number: 10'.
print:
#for number in 10 11 12 13 14 15; do \
( echo "Number: $$number" ); \
break ; \
( echo Break not working ); \
done
As a side note, no need to put 'echo' statements in '( ... )'. It make the script fork unnecessary instances o bash.

Related

Retry a command only once : when a command fails (in bash)

for ( i=3; i<5; i++)
do
execute some command 1
if command 2 is successful then do not run the command 1 (the for loop should continue)
if command 2 is not successful then run command 1 only once (like retry command 1 only once, after this the for loop should continue)
done
This is to note that command 2 is dependent on command 1 and command 2 can only be executed after command 1
for example:
for ( i=3; i<5; i++)
do
echo "i" >> mytext.txt ---> command 1
if "check the content of mytext.txt file to see if the value of i is actually added" ---> command 2
if it is not added then execute echo "i" >> mytext.txt (command 1) again and only once.
if i value is added to the file .. then exit and continue the loop
done
Since the "command 1" is quite big and not just an example echo statement here.I do not want to add "command 1" twice .. once outside and once inside the if condition. I want this logic in an optimized way with no redundancy of code.
Per a comment it sounds like the OP may need to invoke command 1 up to 2 times for a given $i value, but only wants to type command 1 once in the script.
Siddhartha's suggestion to use a function is probably good enough but depending on the actual command 1 (OP mentions that it's 'quite big') I'm going to play devil's advocate and assume there could be additional issues with passing some args to the function (eg, a need to escape some characters ... ??).
The general idea is to have an internal loop that can be executed at most 2 times, with logic in the loop that will allow for an 'early' exit (eg, after just one pass through the loop).
Since we're using pseudo-code I'll use the same ...
for ( i=3; i<5; i++ )
do
pass=1 # reset internal loop counter
while ( pass -le 2 )
do
echo "i" >> mytext.txt # command 1
if ( pass -eq 1 ) # after first 'command 1' execution
&& ( value of 'i' is in mytext.txt ) # command 2
then
break # break out of inner loop; alternatively ...
# pass=10 # ensure pass >= 2 to force loop to exit on this pass
fi
pass=pass+1 # on 1st pass set pass=2 => allows another pass through loop
# on 2nd pass set pass=3 => will force loop to exit
done
done
you can declare functions like
function command
{
your_command -f params
}
for ( i=3; i<5; i++)
do
if command ; then
echo "success"
else
echo "retry"
command
fi
done

Bash. Return two function levels (two nested calls)

I need to know if Bash has some solution for my case. I need after some conditions to do a "double return". I mean, to perform a return of a function and also return the parent function to skip the rest of the code of that parent function.
I know that I can do a conditional using function return values to achieve this. But I'd like to know if in Bash exist something like "break 2" for functions. I don't want if possible to modify the code of the parent function because as you can imagine, in my real script there are dozens of functions and I don't want to modify all of them.
Example:
#!/bin/bash
function sublevelone() {
echo "sublevelone"
# Return 2, or break 2 or something :D
}
function main() {
sublevelone
echo "This is the part of the code to being avoid executed"
}
main
I don't know what the bash experts will think, but this works at least for simple cases:
multireturn(){
[ -n "$1" ] && poplevel="$1"
if [ "$poplevel" -ge 0 ]; then
trap multireturn DEBUG
shopt -s extdebug
(( poplevel-- ))
return 2
else
shopt -u extdebug
trap - DEBUG
return 0
fi
}
This makes use of the DEBUG trap and the extdebug flag:
extdebug
If set at shell invocation, arrange to execute the
debugger profile before the shell starts, identical to
the --debugger option. If set after invocation, behav-
ior intended for use by debuggers is enabled:
1. The -F option to the declare builtin displays the
source file name and line number corresponding to
each function name supplied as an argument.
2. If the command run by the DEBUG trap returns a
non-zero value, the next command is skipped and
not executed.
3. If the command run by the DEBUG trap returns a
value of 2, and the shell is executing in a sub-
routine (a shell function or a shell script exe-
cuted by the . or source builtins), the shell
simulates a call to return.
4. BASH_ARGC and BASH_ARGV are updated as described
in their descriptions above.
5. Function tracing is enabled: command substitu-
tion, shell functions, and subshells invoked with
( command ) inherit the DEBUG and RETURN traps.
6. Error tracing is enabled: command substitution,
shell functions, and subshells invoked with (
command ) inherit the ERR trap.
Example usage:
#!/bin/bash
multireturn(){
[ -n "$1" ] && poplevel="$1"
if [ "$poplevel" -ge 0 ]; then
trap multireturn DEBUG
shopt -s extdebug
(( poplevel-- ))
return 2
else
shopt -u extdebug
trap - DEBUG
return 0
fi
}
# define 8 levels of function calls
# (level N prints output, calls level N+1, then prints more output)
for i in $(seq 1 8); do
eval \
'level'$i'(){
echo -n " '$i'"
level'$((i+1))'
echo -n "('$i')"
}'
done
# final level calls multireturn
level9(){
echo -n " 9"
multireturn $n
echo -n "(9)"
}
# test various skip amounts
for i in $(seq 0 10); do
echo -n "$i:"
n=$i
level1
echo .
done
echo
echo done
Result:
0: 1 2 3 4 5 6 7 8 9(9)(8)(7)(6)(5)(4)(3)(2)(1).
1: 1 2 3 4 5 6 7 8 9(8)(7)(6)(5)(4)(3)(2)(1).
2: 1 2 3 4 5 6 7 8 9(7)(6)(5)(4)(3)(2)(1).
3: 1 2 3 4 5 6 7 8 9(6)(5)(4)(3)(2)(1).
4: 1 2 3 4 5 6 7 8 9(5)(4)(3)(2)(1).
5: 1 2 3 4 5 6 7 8 9(4)(3)(2)(1).
6: 1 2 3 4 5 6 7 8 9(3)(2)(1).
7: 1 2 3 4 5 6 7 8 9(2)(1).
8: 1 2 3 4 5 6 7 8 9(1).
9: 1 2 3 4 5 6 7 8 9.
10: 1 2 3 4 5 6 7 8 9
done
This is kinda whacky but if you use parentheses to define levelone, it will execute the function in a subshell and then you can exit out of that shell from an inner function. That said, I think it's more appropriate to use return to send back value that you check for in the parent function.
#!/bin/bash
function leveltwo() {
echo "two"
exit
}
function levelone() (
echo "one"
leveltwo
echo "three"
)
levelone
echo "four"
Will print:
one
two
four
Why don't you return an exit status from the function and add an if-statement to main like you normally would in any other programming/scripting language?
#!/bin/bash
function sublevelone() {
echo "sublevelone"
[ 0 -eq 1 ]
return $? # Returns 1 in this case because 0 != 1
}
function main() {
sublevelone
[ $? -eq 0 ] || return 1 # Return in case of error
echo "This is the part of the code to being avoid executed"
}
main
exit 0
Just a short version of #jhnc answer for exactly 2-level return from a function:
trap 'trap "shopt -u extdebug; trap - DEBUG; return 0" DEBUG; return 2' DEBUG
shopt -s extdebug
return

What's the difference between these two bash parallelization syntax?

Value "4" below is the number of CPU threads. Idea is to run the tasks in batch of 4 and wait until the current batch is finished before starting the next batch.
Syntax 1:
while read something; do
((++i%4==0)) && wait
(
task using something as input;
)
done < input_file.txt
Syntax 2:
while read something; do
((i=i%4)); ((i++==0)) && wait
(
task using something as input;
)
done < input_file.txt
To me they both work the same except the second one is longer. But when running in the cloud (AWS ubuntu 14.04), only syntax 1 worked. The syntax2 threw a generic syntax error at "((i=i%4));" step and it became a mystery.
"The second one is longer" doesn't help since you used pseudocode.
Maybe this will help:
while read x; do ((i=++i%4)) || wait; sleep $x & done < input_file.txt
My input_file.txt:
10
9
8
7
6
5
4
3
2
1

Dynamic variable syntax with delayed expansion

In an attempt to reuse code I am trying to use dynamic variables to test a condition but am unable to get the result I need. I'm using delayed expansion.
1 Outside the for loop:
2 set "H_HEADER=FALSE"
3 set "SUB_TRANSTYPE=#"
4
5 Inside the for loop:
6 set "SUB_TRANSTYPE=!FULL_LINE:~0,1!"
7 if !SUB_TRANSTYPE!==H (
8 echo sub_transtype_header is !!SUB_TRANSTYPE!_HEADER!
9 )
Line 6 sets SUB_TRANSTYPE to H
Line 8 prints H_HEADER to console but I want it to print FALSE (the value of H_HEADER)
I've messed around with escape characters but can't get this to work. Help!
Line 6 should be:
set "SUB_TRANSTYPE=!FULL_LINE:~0,1!"
Line 7 should be:
if !SUB_TRANSTYPE!==H (
Line 8 should be:
for /F %%A in ("!SUB_TRANSTYPE!") do echo sub_transtype_header is !%%A_HEADER!
This type of management is fully described at this answer.
if !SUB_TRANSTYPE!==H (
CALL echo sub_transtype_header is %%!SUB_TRANSTYPE!_HEADER%%
)
note that you have a = missing from the == operator.
This executes the echo in a subshell.
To interpret the value in an if statement, use
call set "someothervariable=%%!SUB_TRANSTYPE!_HEADER%%"
if "!someothervariable!"=="value" (

Bash script, check numeric variable in in range

This is my script:
#!/bin/bash
JOB_NUM=4
function checkJobNumber() {
if (( $JOB_NUM < 1 || $JOB_NUM > 16 )); then
echo "pass"
fi
}
...
checkJobNumber
...
When I try to launch the script I get the message:
./script.sh line 49: ((: < 1 || > 16 : syntax error: operand expected (error token is "< 1 || > 16 ")
(Please notice the spaces in the error message)
I really don't understand what the problem is. If I do the evaluation manually at the command line, it works.
Also, I tried different evaluations like if [[ "$JOB_NUM" -lt 1 -o "$JOB_NUM" gt 16 ]];... still no success.
UPDATE: As suggested I made few more attempts in the rest of the code outside the function call, and I found the problem.
My variables declaration was actually indented this way:
JOB_NUM= 4
THREAD_NUM= 8
.....
VERY_LONG_VAR_NAME= 3
and apparently this hoses the evaulation. BAH! It always worked for me before, so why doesn’t it now?
If I delete the white spaces, the evaluation works:
JOB_NUM=4
THREAD_NUM=8
....
VERY_LONG_VAR_NAME=3
OK I officially hate bash . . . sigh :(
this will work fine
#!/bin/bash
JOB_NUM=7
function checkJobNumber() {
if [ $JOB_NUM -lt 0 ] || [ $JOB_NUM -gt 16 ] ; then
echo "true"
else
echo "false"
fi
}
checkJobNumber
And if you want to check variable during tests you can write in your bash :
set -u
this will generate a message like "JOB_NUM: unbound variable" if variable is not well set

Resources