I just started learning Bash scripting and i have to do a program that separate between one bit map image to two (the image is broken), I already found on the web how to write loops and statements
but i don't know why my if statement is always goes to the else.
the if is modulo by 2 thats equals to 0
here is the following code
#!/bin/sh
OUTPUT="$(hexdump -v -e '/1 "%02X\n"' merge.bmp)"
echo $OUTPUT
vars=0
count=1
touch one
touch two
for i in $OUTPUT
do
if (($vars%2==0))
then
echo "1"
else
echo "2"
fi
vars=$((vars+count))
done
in the terminal the following error is
./q3.sh: 14: ./q3.sh: 2885%2==0: not found
2
i really don't know why the if always print 2
The shebang line is wrong, it should be:
#!/bin/bash
((expression)) is a bash extension, not available in sh.
The /bin/sh version of the (()) bashism is this:
if test $(($vars % 2)) -eq 0; then
echo "1"
...
fi
Since $(()) knows about variable names, you may even drop the dollar and write
if test $((vars % 2)) -eq 0; then
echo "1"
...
fi
Related
This question already has answers here:
Why should there be spaces around '[' and ']' in Bash?
(5 answers)
Closed 5 years ago.
I am fairly new to bash scripting and am struggling with some if-statement syntax.
I have currently written up the following loop:
for (( i = 2; i < $# - 1; i++)); do
if [ $i -ne 0]; then
if [ $i -ne 1]; then
echo "$i was not 1 or 0. Please correct this then try again."
exit 1;
fi
fi
done
This code is supposed to test whether any arguments after the first are either a 1 or a 0.
While the following errors are printed:
./blink.sh: line 36: [: missing `]'
./blink.sh: line 36: [: missing `]'
...the code actually runs fine afterwards (so the errors don't kill the program).
My understanding, however, is that in bash, you put spaces before and after the expression inside the if statement. So this:
if [ $i -ne 0]; then
Becomes:
if [ $i -ne 0 ]; then
However, running this code produces the following:
2 was not 1 or 0. Please correct this then try again.
The main issue I am having with this stems from not understanding how to indirectly reference the positional arguments provided by the execution command. As such, I am confused as to what syntax must be altered to call the objects the arguments point to (in this case, hopefully either a 1 or a 0) rather than the position of the arguments themselves (argument 1, 2, 3...).
Thanks!
EDIT: Altering the question to better fit the advice #randomir provided and clear up what the actual question entails
Based on:
This code is supposed to test whether any arguments after the first are either a 1 or a 0.
I'm assuming you're trying to access positional arguments $2, $3, etc. To make your for loop solution work, you would have to use an indirect reference: ${!i} (see shell parameter expansion). For example, this should work:
#!/bin/bash
for (( i = 2; i <= $#; i++ )); do
if [[ ${!i} -ne 0 ]]; then
if [[ ${!i} -ne 1 ]]; then
echo "$i was not 1 or 0. Please correct this then try again."
exit 1;
fi
fi
done
Note the i running from 2 to number of arguments $#. Also, note the use of recommended and less error-prone [[ .. ]] instead of [ .. ] (otherwise you would have to write [ "${!i}" -ne 0 ], etc).
A simpler solution which avoids the unnecessary indirect referencing looks like this:
#!/bin/bash
while [[ $2 ]]; do
if (( $2 != 0 && $2 != 1 )); then
echo "$2 is neither 0, nor 1"
exit 1
fi
shift
done
We start checking the second argument ($2), use the arithmetic expression (( expr )) testing of value of the second argument, and shift positional arguments to the left by 1 at each iteration (now $3 becomes $2, etc).
I am a little confused about Bash test expression and if test commands:
(1)
#!/bin/bash
count=90
if [ $((count)) ]; then
echo "True "
else
echo "False"
fi
Executing it:
# ./test1.sh
True
(2)
#!/bin/bash
count=90
if $((count)); then
echo "True "
else
echo "False"
fi
Executing it:
# ./test2.sh
./test2.sh: line 5: 90: command not found
False
(3)
#!/bin/bash
count=90
if ((count)); then
echo "True "
else
echo "False"
fi
Executing it:
# ./test3.sh
True
My questions are as follows:
a) For test2.sh, why does it complain "command not found"?
b) Although test1.sh and test3.sh outputs the same result, do they have same meanings in if condition?
Anything that does not produce an error is valid.
That said, there are conventions that people follow when writing shell scripts, and they exist mostly because they make sense.
The if command is a construct that:
runs a command, then on the basis of the exit code of that command,
runs some other command.
For example, your system has programs named true and false on it. So you can make an if construct like:
if true; then
echo TRUE
else
echo FALSE
fi
When you "wrap a condition in square brackets" in shell, what you're really doing is running a command named [. It may be a built-in in your shell, or it may be located at /bin/[, but it's a command either way. Its options appear like conditions, and its purpose is to produce an exit value that will be consumed by the if command.
Now ... when you do arithmetic in bash, you can use constructs like $((...)) which is called "Arithmetic Expansion" because the result of the arithmetic is expanded to replace the expression, as if it were a variable. When you use ((...)), without the preceding dollar sign, then the expression is simply evaluated, rather than printed.
So .. Your first if command tests to see that the arithmetic expansion evaluates to true. Most valid arithmetic should do this. Your second command executes the expansion as if it were a command, which is not. And you get the error telling you that 90 can't be run as a command. And your third command executes the arithmetic, but without expanding it. As with the first option, as long as the arithmetic is valid, the test returns true.
The difference between the first and the third variants is that in the first case, the test command (a.k.a. /bin/[ or your shell's built-in equivalent) is evaluating the results of your arithmetic, the result of which is always true unless you do something silly like try to use decimal numbers, whereas in the third case, a valid arithmetic expression that results in a 0 (zero) will appear to be "false".
To test this difference in your shell, try the following:
$ if [ $(( 2.5 + 2 )) ]; then echo yes; else echo no; fi # ERROR
$ if (( 2.5 + 2 )); then echo yes; else echo no; fi # ERROR, no
$ if [ $(( 2 + 2 )) ]; then echo yes; else echo no; fi # yes
$ if (( 2 + 2 )); then echo yes; else echo no; fi # yes
$ if [ $(( 2 - 2 )) ]; then echo yes; else echo no; fi # yes
$ if (( 2 - 2 )); then echo yes; else echo no; fi # no
Either behaviour may be what you're looking for, but you haven't indicated what problem you're trying to solve, so I can't recommend one over the other.
I'm trying to execute this simple script in solaris.
I want to sort(numeric) the filenames of the files in source directory and copy the file one by one to another directory. And, I want to print a message after copying every 100 files.
#!/bin/bash
count=0
for i in `ls | sort -n`
do
cp $i ../target
count = $((count+1))
if[ $count%100 -eq 0 ]
then
echo $count files copied
sleep 1
fi
done
this is not working. I tried different things after searching in net.
I get errors like these -
syntax error at line 8: '(' unexpected.
syntax error at line 10: 'then' unexpected.
syntax error at line 13: 'fi' unexpected etc.
What is the problem with this script?
bash version - GNU bash, version 3.00.16(1)-release (sparc-sun-solaris2.10)
The basic problem with the script is spacing. You have spaces where you shouldn't have them:
(wrong) count = $((count+1))
(right) count=$((count+1))
(better) ((count++))
and you're missing spaces where you need them:
(wrong) if[ $count%100 -eq 0 ]
(right) if [ $((count % 100)) -eq 0 ]
(better) if ((count % 100 == 0))
count = $((count+1)) tries to run the command count passing it two arguments, = and the value of count+1. if[ ... tries to run the command if[ because [ is a valid word character; it doesn't automatically start a new token.
Having said all that, I'm puzzled by the unexpected ( error message. Could your bash be too old to recognize $(( syntax? Possibly. It's a very old bash.
count=$((count+1))
if [ `echo $count % 100 | bc` -eq 0 ]
Make these corrections.
Edit: Please try
count=`expr $count + 1`
I see a few errors here. First, you need double quotes around $i in case they have special characters.
Second, you shouldn't ever use
for i in `ls | sort -n`
Instead, try the following
ls -1 | sort -n | while read i
Third, change your if statement to
if ((count%5 == 0))
The (( syntax is bash is made just for integer math.
Fourth, change your counter increment to
((count++))
This is more concise. Also, the space in your version may break things. Remember, spaces matter.
I've been arguing with this code all morning. It finally dawned on me, upon reading TL;DP that my if statements may be confusing output redirection with a comparison.
The code in question is:
#!/bin/sh
...
if [ $DEBUG_LEVEL > 2 ]
then
echo "I made it here"
echo "DEBUG: created run_all_somatic_SNV_steps" >>$LOG
fi
Is my if statement confusing stderr redirection with what I want it to do? (compare a variable to the number 2)
For general shell:
if [ $DEBUG_LEVEL -gt 2 ]
(But that will fail if DEBUG_LEVEL has never been set.)
More bash-specific, and a lot nicer:
if (( DEBUG_LEVEL > 2 ))
try this test construct:
[ $DEBUG_LEVEL -gt 2 ]
I'll get to the meat and bones:
MY_VAR=6
until [$MY_VAR = 0]
do
dir/dir_$MY_VAR.log
ps | grep "NAME_$MY_VAR.ksh"
check some things
if [results = ok]
echo "program $MY_VAR sucessful"
else
echo "program $MY_VAR failure"
MY_VAR = `expr $MY_VAR - 1`
done
Now I am getting the following errors MY_VAR not found and [6: not found, so I'm assuming a rather noobish mistake. I feel the logic is sound enough just a simple syntax error I am making somewhere by the looks of the two errors I think it could be in the declaration.
You need to have a space after [ and before ] since [ is actually a command and not a delimiter.
Here is your script re-written in Bash (or ksh):
my_var=6
until ((my_var == 0))
do
dir/dir_$my_var.log # I have no idea what this is supposed to be
ps | grep "NAME_$my_var.ksh"
# check some things
if [[ $results = ok ]]
then
echo "program $my_var successful"
else
echo "program $my_var failure"
((my_var--))
fi
done
However:
for my_var in {6..1}
do
dir/dir_$my_var.log # I have no idea what this is supposed to be
ps | grep "NAME_$my_var.ksh"
# check some things
if [[ $results = ok ]]
then
echo "program $my_var successful"
else
echo "program $my_var failure"
fi
done
Your two errors are caused by:
until [$MY_VAR = 0]
MY_VAR = $(expr $MY_VAR - 1)
[I've used $() instead of backticks because I couldn't get backticks into the code section]
The first problem is the lack of spaces around the square brackets - on both ends. The shell is looking for the command [6 (after expanding $MY_VAR), instead of [ (have a look at /usr/bin/[ - it's actually a program). You should also use -eq to do numeric comparisons. = should work ok here, but leading zeros can break a string comparison where a numeric comparison would work:
until [ "$MY_VAR" -eq 0 ]
The second problem is you have spaces in your variable assignment. When you write MY_VAR = ... the shell is looking for the command MY_VAR. Instead write it as:
MY_VAR=`expr $MY_VAR - 1`
These answers directly answer your questions, but you should study Dennis Williamson's answer for better ways to do these things.