I'm trying to run a bash script that includes a nested for loop within which a variable should cycle through negative exponents, viz:
for ABPOW in {-11..-9}
do
ABC = $((10**$ABPOW))
for ABCOEFF in {1..9}
do
sed -e 's/ACOEFF/'$ABC'/'\
This is only the inner two for loops of the code. When the values in the first bracket (for ABPOW) are positive, the code runs fine. However, when I have them as i do above, which is what I need, the error communicated to screen is:
./scripting_test2.bash: line 30: 10**-11: exponent less than 0 (error token is "1")
How do I make this run? Thanks in advance.
PS: I tried putting a negative sign in front of $ABPOW but the exponents are still recorded as positive.
Bash does not support floating point arithmetic (which is necessary for raising something into a negative power). Instead, you should use the bc utility.
ABC=$(bc -l <<< "10 ^($ABPOW)")
Also, there should be no spaces before and after the = in variable assignments
Here's another way:
perl -e 'for $abpow (-11..-9) { $abc=10**$abpow; for (1..9) { system("echo $abc"); } }'
Bash doesn't support floating point data types.
$ man bash|grep -c -i float
0
Do you have python installed?
ABC=$( python -c "print 10**$ABPOW" )
One alternative is to use awk for your complete script that has full support for floating point arithmetic:
awk 'BEGIN{for(i=-11; i<=-9; i++) {ABC=10^i; print ABC}}'
1e-11
1e-10
1e-09
Related
I'm new to Bash and Linux. I'm trying to make it happen in Bash. So far my code looks smth like this:
The problem is with coping using awk or sed, i suppose... as i try to put the sum of a constant and a variable somehow wrong. Brackets or different quotes do not make any difference.
for i in {0..10}
do
touch /path/file$i
awk -v "NR>=1+$i*9 && NR<=$i*9" /path/BigFile > /path/file$i
(or with sed) sed -n "1+$i*9,$i*9" /path/BigFile > /path/file$i
done
Thank you in advance
Instead of reinventing this wheel, you can use the split utility. split -l 10 will tell it to split into chunks of 10 lines (or maybe less for the last one), and there are some options you can use to control the output filenames -- you probably want -d to get numeric suffixes (the default is alphabetical).
hobbs' answer (use split) is the right way to solve this. Aside from being simpler, it's also linear (meaning that if you double the input size, it only takes twice as long) while these for loops are quadratic (doubling the input quadruples the time it takes), plus the loop requires that you know in advance how many lines there are in the file. But for completeness let me explain what went wrong in the original attempts.
The primary problem is that the math is wrong. NR>=1+$i*9 && NR<=$i*9 will never be true. For example, in the first iteration, $i is 0, so this is equivalent to NR>=1 && NR<=0, which requires that the record number be at least 1 but no more than 0. Similarly, when $i is 1, it becomes NR>=10 && NR<=9... same basic problem. What you want is something like NR>=1+$i*9 && NR<=($i+1)*9, which matches for lines 1-9, then 10-18, etc.
The second problem is that you're using awk's -v option without supplying a variable name & value. Either remove the -v, or (the cleaner option) use -v to convert the shell variable i into an awk variable (and then put the awk program in single-quotes and don't use $ to get the variable's value -- that's how you get shell variables' values, not awk variables). Something like this:
awk -v i="$i" 'NR>=1+i*9 && NR<=(i+1)*9' /path/BigFile > "/path/file$i"
(Note that I double-quoted everything involving a shell variable reference -- not strictly necessary here, but a general good scripting habit.)
The sed version also has a couple of problems of its own. First, unlike awk, sed doesn't do math; if you want to use an expression as a line number, you need to have the shell do the math with $(( )) and pass the result to sed as a simple number. Second, your sed command specifies a line range, but doesn't say what to do with it; you need to add a p command to print those lines. Something like this:
sed -n "$((1+i*9)),$(((i+1)*9)) p" /path/BigFile > "/path/file$i"
Or, equivalently, omit -n and tell it to delete all but those lines:
sed "$((1+i*9)),$(((i+1)*9)) ! d" /path/BigFile > "/path/file$i"
But again, don't actually do any of these; use split instead.
I'm trying to figure out a way to take the result of a basic equation and divide by another integer. So in in simple form it looks like;
A-B = C and then divide C by D
I understand the first part of the equation is echo $((A-B)) but how do I get that result divided by D?
Any feedback would be greatly appreciated!
Using shell
This (taken from the comments) fails because the parentheses are unbalanced:
$ echo $((2147483633-807279114))/5184000))
Try instead:
$ echo $(( (2147483633-807279114)/5184000 ))
258
The above returns an integer because the shell only does integer arithmetic.
Using bc
If you want accurate floating-point numbers, the standard tool to use is bc:
$ echo '(2147483633-807279114)/5184000' | bc -l
258.52710628858024691358
Using python
Python supports both integer and floating point arithmetic:
$ python -c 'print (2147483633-807279114)//5184000'
258
$ python -c 'print (2147483633-807279114)/5184000.0'
258.527106289
Python, with its numpy extension, is an excellent tool even if your needs extend to complex scientific calculations.
Using awk
awk, a standard unix tool, supports floating-point math:
$ awk 'BEGIN{print (2147483633-807279114)/5184000;quit}'
258.527
You can do calculations inside $(( ))
If you want to calculate (A-B)/C, you can put that inside $(( )):
echo $(( (A-B)/C ))
Note that the result will be truncated towards zero, because bash does not support decimals.
Remember to make sure that you have $(( before the expression and )) after, and that all the parentheses inside match up.
dc <<< "$A $B - $D / p"
Note 1: p prints the final result
Note 2: With dc you need to first enter the numbers and then operation
I need some help with a bash mathematical expression script. I need the expression below to be a negative 2 to the power of 63 but I have tried all sorts of combinations from "", '', (()) and even keeping the positive value but multiplying it by -1 but I can't seem to get the syntax right.
Original expression:
kw=`expr 2^63 | bc`
gives me 9223372036854775808 (which is correct) but I need to get a -9223372036854775808.
I want to be able to do a kw=expr -2^63 | bc to get a negative results but it's not as straight-forward as I'd hoped. Hence my numerous attempts of different permutations to the expression.
Any help will be very appreciated.
Here you go:
$ kw=$(echo -2^63 | bc)
$ echo $kw
-9223372036854775808
UPDATE
#DigitalTrauma is right, if you're in bash, then using a bash here string is better (one less process, more efficient):
kw=$(bc <<< -2^63)
Since this is bash, you don't even need the echo; you can use a bash here string instead:
$ kw=$(bc <<< -2^63)
$ echo $kw
-9223372036854775808
Below is my script
#!/bin/sh
typeset resl=$(($1+$2))
echo $resl
when i am passing two value 173591451 and 2000252844 to shell script, it is returning negative value.
./addvalue.sh 173591451 2000252844
output ---> -2121123001
Please let me know how we can fix this problem?
Dropping into a friendly programming calculator application to look at your values in hex I see you are into 32-bits of precision. Once you hit 32-bits (8'th digit >= 8) you have exceeded the size of integer your shell was compiled with and entered the land of negative numbers (but that's another post).
0x81923B47 = 0xA58CB9B + 0x77396FAC
Two workarounds, without having to worry about getting a 64-bit shell, follow.
1. awk
The success of this depends on how your awk as compiled and which awk you are using.
awk 'END {print 173591451 + 2000252844}' </dev/null
Also do all your relational testing in awk.
2. dc
The "dc" program (desk calculator) uses arbitrary precision so you never need to worry about integer bit-size again. To put it into a variable:
$ sum="$( echo 173591451 2000252844 + p | dc )"; echo $sum
2173844295
And avoid typeset -i with dc as the shell needs to see strings. Properly checking relationships (if $a < $b) gets a little tricky, but can be done ($a -lt $b is wrong).
I wrote a .sh file to compile and run a few programs for a homework assignment. I have a "for" loop in the script, but it won't work unless I use only integers:
#!/bin/bash
for (( i=10; i<=100000; i+=100))
do
./hw3_2_2 $i
done
The variable $i is an input for the program hw3_2_2, and I have non-integer values I'd like to use. How could I loop through running the code with a list of decimal numbers?
I find it surprising that in five years no one ever mentioned the utility created just for generating ranges, but, then again, it comes from BSD around 2005, and perhaps it wasn't even generally available on Linux at the time the question was made.
But here it is:
for i in $(seq 0 0.1 1)
Or, to print all numbers with the same width (by prepending or appending zeroes), use -w. That helps prevent numbers being sent as "integers", if that would cause issues.
The syntax is seq [first [incr]] last, with first defaulting to 1, and incr defaulting to either 1 or -1, depending on whether last is greater than or less than first. For other parameters, see seq(1).
you can use awk to generate your decimals eg steps of0.1
num=$(awk 'BEGIN{for(i=1;i<=10;i+=0.1)print i}')
for n in $num
do
./hw3_2_2 $n
done
or you can do it entirely in awk
awk 'BEGIN{cmd="hw3_2_2";for(i=1;i<=10;i+=0.1){c=cmd" "i;system(cmd) } }'
The easiest way is to just list them:
for a in 1.2 3.4 3.11 402.12 4.2 2342.40
do
./hw3_2_2 $a
done
If the list is huge, so you can't have it as a literal list, consider dumping it in a file and then using something like
for a in $(< my-numbers.txt)
do
./hw3_2_2 $a
done
The $(< my-numbers.txt) part is an efficient way (in Bash) to substitute the contents of the names file in that location of the script. Thanks to Dennis Williamson for pointing out that there is no need to use the external cat command for this.
Here's another way. You can use a here doc to include your data in the script:
read -r -d '' data <<EOF
1.1
2.12
3.14159
4
5.05
EOF
for i in "$data"
do
./hw3_2_2 "$i"
done
Similarly:
array=(
1.1
2.12
3.14159
4
5.05
)
for i in "${array[#]}"
do
./hw3_2_2 "$i"
done
I usually also use "seq" as per the second answer, but just to give an answer in terms of a precision-robust integer loop and then bc conversion to a float:
#!/bin/bash
for i in {2..10..2} ; do
x=`echo "scale=2 ; ${i}/10" | bc`
echo $x
done
gives:
.2
.4
.6
.8
1.0
bash doesn't do decimal numbers. Either use something like bc that can, or move to a more complete programming language. Beware of accuracy problems though.