Bash: How to Perform Arithmetic on Numbers in a Pipe

BASH: how to perform arithmetic on numbers in a pipe

echo 1 2 3 4 5|{
read line;
for i in $line;
do
echo -n "$((i * i)) ";
done;
echo
}

The {} creates a grouping. You could instead create a script for that.

Best way to divide in bash using pipes?

Using bc:

$ bc -l <<< "scale=2;$(find . -name '*.mp4' | wc -l)/3"
2.33

In contrast, the bash shell only performs integer arithmetic.

Awk is also very powerful:

$ find . -name '*.mp4' | wc -l | awk '{print $1/3}'
2.33333

You don't even need wc if using awk:

$ find . -name '*.mp4' | awk 'END {print NR/3}'
2.33333

How to do arithmetic operations on value returned by uniq -c?

You might be interested in a single awk script that does all the above in a single go (with exception of the sorting). Also, awk is the tool to use if you want to do arithmetic operations.

awk 'BEGIN{ ere="[0-9][0-9]/[A-Z][a-z][a-z]/[0-9][0-9][0-9][0-9]" }
(match($0,ere)){ date=substr($0,RSTART,RLENGTH); a[date]++; n++ }
END { for (date in a) {
yyyy=substr(date,8,4);
mm=(index("JanFebMarAprMayJunJulAugSepOctNovDec",substr(date,4,3))+2)/3
mm=sprintf("%0.2d",mm)
dd=substr(date,1,2)
print a[date],a[date]/n,yyyy"-"mm"-"dd
}
}' log.txt

As you mention that you only want to do this with Linux/Unix Shell commands, I will take the freedom to assume you mean bash. There are many other types of shell, but let us take the most common one.

Well in short, No, it is not possible. bash does not support floating point arithmetic but you can fake it. Example:

$ echo $(( 2/3 ))
1
$ printf "%f\n" "$(( 10**15 * 2 / 3 ))E-15"
0.666667

So assuming you have your presented output and lets also assume you have to total, you can do:

# total number of dates
n=3417

grep -E [0-9][0-9]/[A-Z]{1}[a-z]{2}/[0-9]{4} log.txt | sed 's/.*\(..\)\/Oct\/\(....\).*/\2-10-\1/' | sort | uniq -c | sort -ru | head -10 | \
while read -r count date; do
printf "%f %d %s\n" "$(( 10**15 * count / n ))E-15" "$count" "$date"
done

But since you already use sed, I still would suggest awk

Arithmetic operations using numbers from grep

Assumptions:

  • we want to match on lines that start with the string number
  • we will always find 2 matches for ^number from the input file
  • not interested in storing values in an array

Sample data:

$ cat file.dat
number1: 123
not a number: abc
number: 456

We'll use awk to find the desired values and print all to a single line of output:

$ awk '/^number/ { printf "%s ",$2 }' file.dat
123 456

From here we can use read to load the variables:

$ read -r num1 num2 < <(awk '/^number/ { printf "%s ",$2 }' file.dat)

$ typeset -p num1 num2
declare -- num1="123"
declare -- num2="456"

$ echo ".${num1}.${num2}."
.123.456.

NOTE: periods added as visual delimiters

variable error in bash when doing calculation

You received that error because you passed an invalid arithmetic expression into a bash arithetic expansion. Only an arithmetic expression is allowed for this place. What you try to do seems like this:

ref="$(grep -v ">" /data/ref/EN | wc -c)"
sample="$(grep -v ">" /example/SR | wc -l)"

# this is only integer division
#u=$(( sample / ref ))
#z=$(( 100 * u ))

# to do math calculations, you can use bc
u=$(bc <<< "scale=2; $sample/$ref")
z=$(bc <<< "scale=2; 100*$u")

printf "%d, %d, %.2f, %.2f\n" "$ref" "$sample" "$u" "$z"

so hopefully you get an output like this:

41858, 38986, 0.93, 93.00

Notes:

  • There is no need to cd before executing a grep, it accepts the full path with the target filename as an argument. So without changing directory, you can grep various locations.

  • In order to save the output of your command (which is only a number) you don't need to save it in a file and cat the file. Just use the syntax var=$( ) and var will be assigned the output of this command substitution.

  • Have in mind that / will result to 0 for the division 38986/41858 because it's the integer division. If you want to do math calculations with decimals, you can see this post for how to do them using bc.

  • To print anything, use the shell builtin printf. Here the last two numbers are formatted with 2 decimal points.

How do I use floating-point arithmetic in bash?

You can't. bash only does integers; you must delegate to a tool such as bc.

use shell pipe output as calculator input

As mentioned by Charles that parsing ls is not a good practice but anyway if you want to do that, you can try

 ls *.gz |wc -l | xargs echo "0.5*" | bc

Can't do arithmetic operation in bash

Remove the carriage return \r from the size:

size=${size%$'\r'}

Arithmetic expressions in Bash?

In Bash, let allows multiple assignments on a line:

let a=3 b=4 c=5

As you show in your question, quoting the argument to let allows you to put spaces around the operators. You can, however, omit the quotes if you avoid using spaces.

Another form using double parentheses at the beginning of the statement (instead of the i=$((j + 1)) form) allows you to include spaces around the equal sign or do post- or pre- increment or decrement and additional assignment operations:

(( a = ( b + c ) * 4 ))
(( count++ ))
(( d = --c**2 ))
(( e *= 2 ))
(( f = 3, g = 5 )) # multiple operations require a comma separator

If you do help "((" it says that the double parentheses is 'Equivalent to "let EXPRESSION".'

You can use the declare builtin to make assignments, including indirectly:

blue=2
name=blue
declare $name=4
echo $blue # result: 4
echo ${!name} # result: 4

Edit:

The $(()) construct is called "arithmetic expansion" and causes the contents to be evaluated as an integer expression. It's a syntax element of the shell.

If a variable is declared as an integer you don't need to use either form of double parentheses, you can omit the dollar sign from the variable name (as in the double-parentheses forms), but you can't add spaces around operators:

declare -i x=1   # set integer, initialize to 1
declare +i s=1 # clear integer, initialize to 1
x+=1 # could also be x=x+1
echo $x # result: 2 (addition)
s+=1 # could also be s=$s+1, requires a "$"
echo $s # result: 11 (string concatenation)

Unlike the forms above, calling expr involves spawning an external executable which can be quite expensive for a lot of calculations in a loop. The only time it should be used is in environments where the shell can't do its own arithmetic or for portability when a script may find its way into such an environment. POSIX shells have arithmetic capability so it would be a concern only with older systems.

Regarding the use of bc for floating point arithmetic, it or something similar is required when using Bash and many other shells. POSIX says that "Only signed long integer arithmetic is required."

Two shells that do support float math are ksh and zsh. In addition to bc, you can use dc, AWK, Python, Perl and others from within a Bash script.

One thing that Bash will do with floating point numbers is print them with the printf builtin (note that there is also an external printf, but builtins have priority).

printf "%'14.4f\n" 1234.56  # result "    1,234.5600" (in my locale)


Related Topics



Leave a reply



Submit