While loop in makefile - linux

I've got following make file:
n ?= 10
all:
while [[ $${n} -gt 0 ]] ; do \
echo $$n ; \
((n = n - 1)) ; \
done
And when I'm trying to run it (make), run fails with error:
make: *** [all] Error 1
I can't understand the reason of this fail.
Any help appreciated.

Just to note: you are causing your makefile to be extremely non-portable. Make always invokes recipes using /bin/sh. The recipe you've written is actually a bash script and won't work with a POSIX /bin/sh; it will only work on systems which use /bin/bash as /bin/sh. Many (most, depending on how you count them) do not.
You can rewrite:
n ?= 10
all:
n=$(n); \
while [ $${n} -gt 0 ] ; do \
echo $$n ; \
n=`expr $$n - 1`; \
done; \
true

A rule examines the last return code. If it is nonzero, make raises an error.
In your case, the last retrun code is the result of (( n = n - 1)) for n=2, i.e. 1.
To avoid the error, simply modify the line 5 to:
((n = n - 1)) || true; \

Related

Check a c file output in Linux

I have 2 files .c which only contain a printf("x")
I am in bash script and i want to check if the values in the printf are for project1.c =20 and for project 2 =10,and then make some changes depending on the values.
How am i supposed to make the comparison in the if command?
This is what i have tried to do,not sure if it is right way.
for d in $1/*/*
do
gcc project1 project1.c
if[ ./project1 = 20 ];then
$project1 =30
else
$project1 =0
fi
gcc project2 project2.c
if[ ./project2 =10 ];then
$project2 = 70
else
$project2 = 0
fi
sum=$project1 + $project2
echo "project1 : $project1 project2: $project2 total grade: $sum" >> grades.txt
done
fi
Your invocation of gcc is wrong. You have to specify the output file:
gcc -o project1 project1.c
Next, in shell, variable substitution is a different process than assignment. So, you can't write $var=foo. The correct syntax is var=foo.
Then, space is a special character (it is used to separate arguments). So var=foo is not the same than var = foo. So, the correct syntax is:
project1=30
Next, in shell, the pattern $(command) is replaced by the result of command. So. I have to do:
if [ $(./project2) == 10 ]; then
Finally, you can do arithmetic using $((calculus)). So, you have to write:
sum=$(($project1 + $project2))

creating multiple user named and numbered files without loop from bash

Totally new in bash programming and got a problem like this:
N empty files should be created. The file sum N and also the file name should be given from users via command line.
no loop and getopts should be used.
I've tried something like this which i found from google but it doesn't works. I got confused between linux and windows bash scripts. Hope someone can help me with the problem. Thank you!
#!/bin/bash
echo $N
:start
if [ $N > 0 ]
then
touch xyzfile_$N
$N = $N - 1
pause
goto start
else
then
echo "no data will be created"
fi
The command line and the result should be (for example if N=7) look like this:
command line:
./createfiles -n filename 7
expected result:
filename_1,filename_2,filename_3...filename_7
You can do this using a recursive shell function:
fn() {
# add some sanity checks to check parameters
touch "$1_${2}"
(($2 > 1)) && fn "$1" $(($2 - 1))
}
This part is a recursive call:
(($2 > 1)) && fn "$1" $(($2 - 1))
Where it basically calls itself by decrementing 1 from 2nd argument as long as $2 is greater than 1.
Then just call it as:
fn filename 7
It will create 7 files as:
filename_1 filename_2 filename_3 filename_4 filename_5 filename_6 filename_7
I ended up the question with a for-loop like this:
createfile() {
name="$1"
num="$2"
for((i=1;i<=num;i++))
do
touch "$1_${i}"
done
}
createfile $1 ${2}
It works great actually. And now I tried to solve the problem with getopts and it seems a function call doesn't work in the case option

Abnormal behavior when parsing an array

Since yesterday I am facing with a strange behavior of shell code.
This is the code:
#!/bin/bash
operatori="/*-+="
temp=$1
len_temp=${#temp}
for (( i=0; i<$len_temp; i++ ))
do
array[i]=${temp:i:1}
#echo 'i= '${array[i]}
done
for i in ${array[#]}
do
if [[ "$operatori" =~ "$i" ]]; then
echo '##'$i
fi
done
It is executed with $1 = tom*jerry-1=0.
In this version of the code , i expect to return:
##*
##-
##=
But it returns just
##-
##=
On the other hand, I tried to deny the condition, having
if ! [[ "$operatori" =~ "$i" ]]; then
But the result is more strange:
##t
##o
##m
##analyzer.sh
##gnome-terminal.desktop
##mount location.sh
##test.sh
##j
##e
##r
##r
##y
##1
##0
Where I expect to receive:
##t
##o
##m
##j
##e
##r
##r
##y
##1
##0
where the analyzer.sh, gnome-teminal.desktop, mount location.sh, test.sh represents some files from the same location where my code is saved.
Can anyone tell me what am I doing wrong?
You should wrap variable references in double-quotes to prevent unexpected parsing oddities. In particular, use for i in "${array[#]}" and echo "##$i".
What's happening here is that in the for statement, the array expands to the equivalent of t o m * j e r r y - 1 = 0, which then undergoes word splitting (ok in this case) and wildcard expansion (which replaces the * with a list of files in the current directory), giving the equivalent of t o m analyzer.sh gnome-terminal.desktop 'mount location.sh' test.sh j e r r y - 1 = 0, which then causes the weird results you're seeing.
You could avoid this problem by setting the noglob shell option (as Kenavoz) suggested), but this will break any parts of the script that depend on wildcard expansion (and still leaves the potential for unexpected word splitting). It's better to just use double-quotes.
You can set bash to noglob as first command of your script to prevent globbing with * :
set -o noglob
Update :
Use set +o noglob to set noglob back to off when your script needs wildcard expansion :
set -o noglob
for i in ${array[#]}
do
if [[ "$operatori" =~ "$i" ]]; then
echo '##'$i
fi
done
set +o noglob
Note : #mikcutu, the noglob solution is a (working) workaround. See #Gordon Davisson's answer for details about why you first should double-quote your variables to prevent word splitting.

How to compare variables in bash

I'm begginer in linux console; I want to create if statement with integer variables
if[$x= [$#-2]]
But console receive if can't find this statment if[1 = [5-2]]
Please help me and correct my statement.
You need Arithmetic Expansion: $((expression))
if [ $x = $(($# - 2)) ]; then
# ^ ^ ^ ^ ^ spaces are mandatory
To start $# is the number of parameters passed to the bash script
./bash_script 1 2 3
$# auto-magically populates to 3. I hope you know this already.
#!/bin/bash
x=1
#If you are trying to compare `$x` with the value of the expression `$# - 2` below is how you do it :
if (( $x == $# - 2 ))
then
echo "Some Message"
fi
#If you are trying to check the assignment to `$x was successful below is how you do it :
if (( x = $# - 2 ))
then
echo "Some Message"
fi
The second condition is almost always true, but the first can be false.
Below are the results of my test runs :
#Here the both ifs returned true
sjsam#WorkBox ~/test
$ ./testmath1 1 2 3
Some Message
Some Message
#Here the first if returned false because we passed 4 parameters
sjsam#WorkBox ~/test
$ ./testmath1 1 2 3 4
Some Message

Problem with bash code

function dec_to_bin {
if [ $# != 2 ]
then
return -1
else
declare -a ARRAY[30]
declare -i INDEX=0
declare -i TEMP=$2
declare -i TEMP2=0
while [ $TEMP -gt 0 ]
do
TEMP2="$TEMP%2"
#printf "%d" "$TEMP2"
ARRAY[$INDEX]=$TEMP2
TEMP=$TEMP/2
INDEX=$[ $INDEX + 1 ] #note
done
for (( COUNT=INDEX; COUNT>-1; COUNT--)){
printf "%d" "${ARRAY[$COUNT]}" <<LINE 27
#echo -n ${ARRAY[$COUNT]} <<LINE 28
}
fi
}
why is this code giving this error
q5.sh: line 27: ARRAY[$COUNT]: unbound variable
same error comes with line 28 if uncommented
One more question, I am confused with the difference b/w '' and "" used in bash scripting any link to some nice article will be helpfull.
It works fine for me except that you can't do return -1. The usual error value is 1.
The error message is because you have set -u and you're starting your for loop at INDEX instead of INDEX-1 (${ARRAY[INDEX]} will always be empty because of the way your while loop is written). Since you're using %d in your printf statement, empty variables will print as "0" (if set -u is not in effect).
Also, it's meaningless to declare an array with a size. Arrays in Bash are completely dynamic.
I would code the for loop with a test for 0 (because the -1 looks confusing since it can't be the index of an numerically indexed array):
for (( COUNT=INDEX - 1; COUNT>=0; COUNT--))
This form is deprecated:
INDEX=$[ $INDEX + 1 ]
Use this instead:
INDEX=$(( $INDEX + 1 ))
or this:
((INDEX++))
I also recommend using lower case or mixed case variables as a habit to reduce the chance of variable name collision with shell variables.
You're not using $1 for anything.

Resources