Bash interpreting increment operation as command - linux

I'm trying to batch modify some images using a bash script, and to print out the progress. It looks to me like bash is interpreting the increment to counter as a command and is giving the following error:
augment_data.sh: line 20: 0: command not found
Here is the code:
for file in *.jpg
do
convert $file -rotate 90 rotated_"$file"
((counter++))
if $((counter % 10 == 0)); then
echo "Rotated $counter files out of $num_files"
fi
done
with line 20 being the one with the counter increment operation.
How can I fix this so that I don't get the error message?

In an arithmetic substitution, the result of an arithmetic operation is substituted in the position of the operation itself.
In this case, $(( 1 == 0 )) has an arithmetic result of 0, and $(( 1 == 1 )) has a result of 1.
Thus, if you use $(( ... )), then this 0 or 1 is substituted in that position, and so gets run as a command. Since you don't have commands named 0 or 1 (probably), either of these will result in a command not found error.
If you use (( ... )), then the arithmetic result directly sets return value, but no expansion takes place.

Related

Bash script : $x=$x+2 is not getting recognised

When I am executing the below script, I am getting the following error :-
The script executes infintely and below line is printed everytime.
"line 9: 1=1+2: command not found". Why?
#!/bin/bash
echo "Script 1 - Linux Scripting Book"
x=1
while [ $x -le 45 ]
do
echo x : $x
$x=$x+2
done
echo "End Of Script 1"
exit 0
Also if I change the $x=$x+2 to x+$x+2 then also I am getting the below error.
line 6: [: 1+2: integer expression expected
Same script when executed like this runs fine.
#!/bin/bash
echo "Script 1 - Linux Scripting Book"
x=1
while [ $x -le 45 ]
do
echo x : $x
let x=x+2
done
echo "End Of Script 1"
exit 0
You get line 9: 1=1+2: command not found because 1=1+2 is what $x=$x+2 is expanded into.
Use expr or let or ((...)) for integer calculations and bc for floating point:
let x=x+2
((x=x+2)) #same as above
((x+=2)) #same
((x++)) #if adding just one
((++x)) #if adding just one
x=$((x+2))
x=`expr $x + 2` #space before and after +
x=$(echo $x+2|bc) #using bc
x=$(echo $x+2.1|bc) #bc also works with floating points (numbers with decimals)
Since this part of the question isn't cleared yet, and not fine to post in a comment, I add this partial answer:
x=1; for i in 1 2 3 ; do x=$x+2; echo $x; done
1+2
1+2+2
1+2+2+2
As a side note: Don't use exit 0 at the end of your script without a good reason. When the script is done, it exits by itself without your help. The exit status will be the exit status of the last command performed, in your case a simple echo, which will almost always succeed. In the rare cases it fails, you will probably without intention hide that failure.
If you source the script, the exit will throw you out of your running shell.
But you can rewrite your while loop like this:
x=0
while (($((x)) < 9))
do
echo x : $x
x=$x+2
done
echo $((x))
x : 0
x : 0+2
x : 0+2+2
x : 0+2+2+2
x : 0+2+2+2+2
10
Because that's not the Bourne shell syntax for setting a variable; it looks more like Perl or PHP. The $ is used for parameter expansion and is not part of the variable name. Variable assignment simply uses =, and let evaluates arithmetic expressions (much like $((expression))). Another syntax that should work is x=$((x+2)). Note that these arithmetic evaluations are a bash feature; standard unix shells might require use of external tools such as expr.

Bash script increments variable, but throws error

I'm trying to process some files in increments of 50. It seems to work, but I'm getting an error that the command isn't found.
File sleepTest.sh:
#!/bash/bin
id=100
for i in {1..5}; do
$((id+=50))
sh argTest.sh "$id"
sleep 2
done
File argTest.sh:
#/bash/bin
id=$1
echo "processing $id..."
The output is
sleepTest.sh: line 6: 150: command not found
processing 150
sleepTest.sh: line 6: 200: command not found
processing 200
sleepTest.sh: line 6: 250: command not found
processing 250
sleepTest.sh: line 6: 300: command not found
processing 300
sleepTest.sh: line 6: 350: command not found
processing 350
So it clearly has an issue with how I'm incrementing $id, but it is still doing it. Why? And how can I increment $id. I tried simply $id+=50, but that did not work at all.
Leave out the $.
((id+=50))
((...)) performs arithmetic. $((...)) performs arithmetic and captures the result as a string. That would be fine if you did echo $((...)), but if you write just $((...)) then the shell treats that number as the name of a command to execute.
var=$((21 + 21)) # var=42
echo $((21 + 21)) # echo 42
$((21 + 21)) # execute the command `42`
Such assignments are legal inside arithmetic expressions. However, bash still tries to interpret the result of the expression as the name of a command. Either pass it as an argument to the : command (the POSIX way)
: $((id+=50))
or use a bash arithmetic statement instead of an arithmetic expression
((id+=50))

Conditional statement not working as expected

I am using Konsole on kubuntu 14.04.
I want to take arguments to this shell-script, and pass it to a command. The code is basically an infinite loop, and I want one of the arguments to the inner command to be increased once every 3 iterations of the loop. Ignoring the actual details, here's a gist of my code:
#!/bin/bash
ct=0
begin=$1
while :
do
echo "give: $begin as argument to the command"
#actual command
ct=$((ct+1))
if [ $ct%3==0 ]; then
begin=$(($begin+1))
fi
done
I am expecting the begin variable to be increased every 3 iterations, but it is increasing in the every iteration of the loop. What am I doing wrong?
You want to test with
if [ $(expr $cr % 3) = 0 ]; then ...
because this
[ $ct%3==0 ]
tests whether the string $ct%3==0, after parameter substitution, is not empty. A good way for understanding this is reading the manual for test and look at the semantics when it is given 1, 2, 3 or more arguments. In your original script, it only sees one argument, in mine it sees three. White space is very important in the shell. :-)
In BASH you can completely utilize ((...)) and refactor your script like this:
#!/bin/bash
ct=0
begin="$1"
while :
do
echo "give: $begin as argument to the command"
#actual command
(( ct++ % 3 == 0)) && (( begin++ ))
done

Bash for loop parameter unexpected behaviour [duplicate]

This question already has answers here:
Variables in bash seq replacement ({1..10}) [duplicate]
(7 answers)
Brace expansion with a Bash variable - {0..$foo}
(5 answers)
Closed 8 years ago.
I'm making a program in bash that creates a histoplot, using numbers I have created. The numbers are stored as such (where the 1st number is how many words are on a line of a file, and the 2nd number is how many times this amount of words on a line comes up, in a given file.)
1 1
2 4
3 1
4 2
this should produce:
1 #
2 ####
3 #
4 ##
BUT the output I'm getting is:
1 #
2 #
3 #
4 #
however the for loop is not recognising that my variable "hashNo" is a number.
#!/bin/bash
if [ -e $f ] ; then
while read line
do
lineAmnt=${line% *}
hashNo=${line##* }
#VVVV Problem is this line here VVVV
for i in {1..$hashNo}
#This line ^^^^^^^ the {1..$hashNo}
do
hashes+="#"
done
printf "%4s" $lineAmnt
printf " $hashes\n"
hashes=""
done < $1
fi
the code works if I replace hashNo with a number (eg 4 makes 4 hashes in my output) but it needs to be able to change with each line (no all lines on a file will have the same amount of chars in them.
thanks for any help :D
A sequence expression in bash must be formed from either integers or characters, no parameter substitutions take place before hand. That's because, as per the bash doco:
The order of expansions is: brace expansion, tilde expansion, parameter, variable and arithmetic expansion and command substitution (done in a left-to-right fashion), word splitting, and pathname expansion.
In other words, brace expansion (which includes the sequence expression form) happens first.
In any case, this cries out to be done as a function so that it can be done easily from anywhere, and also made more efficient:
#!/bin/bash
hashes() {
sz=$1
while [[ $sz -ge 10 ]]; do
printf "##########"
((sz -= 10))
done
while [[ $sz -gt 0 ]]; do
printf "#"
((sz--))
done
}
echo 1 "$(hashes 1)"
echo 2 "$(hashes 4)"
echo 3 "$(hashes 1)"
echo 4 "$(hashes 2)"
which outputs, as desired:
1 #
2 ####
3 #
4 ##
The use of the first loop (doing ten hashes at a time) will almost certainly be more efficient than adding one character at a time and you can, if you wish, do a size-50 loop before that for even more efficiencies if your values can be larger.
I tried this for (( i=1; i<=$hashNo; i++ )) for the for loop, it seems to be working
Your loop should be
for ((i=0; i<hashNo; i++))
do
hashes+="#"
done
Also you can stick with your loop by the use of eval and command substitution $()
for i in $(eval echo {1..$hashNo})
do
hashes+="#"
done

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