Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to divide in bash using pipes?

Tags:

I'm just looking for an easy way to divide a number (or provide other math functions). Let's say I have the following command:

find . -name '*.mp4' | wc -l 

How can I take the result of wc -l and divide it by 3?

The examples I've seen don't deal with re-directed out/in.

like image 875
Brian Avatar asked Nov 01 '12 17:11

Brian


People also ask

Can you pipe in a bash script?

A pipe in Bash takes the standard output of one process and passes it as standard input into another process. Bash scripts support positional arguments that can be passed in at the command line.

How do I break a line in bash?

Linux Files, Users, and Shell Customization with Bash If you want to break up a command so that it fits on more than one line, use a backslash (\) as the last character on the line. Bash will print the continuation prompt, usually a >, to indicate that this is a continuation of the previous line.


2 Answers

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 
like image 124
dogbane Avatar answered Oct 11 '22 17:10

dogbane


Edit 2018-02-22: Adding shell connector

There is more than 1 way:

Depending on precision required and number of calcul to be done! See shell connector further!

Using bc (binary calculator)

find . -type f -name '*.mp4' -printf \\n | wc -l | xargs printf "%d/3\n" | bc -l 6243.33333333333333333333 

or

echo $(find . -name '*.mp4' -printf \\n | wc -l)/3|bc -l 6243.33333333333333333333 

or using bash, result in integer only:

echo $(($(find . -name '*.mp4' -printf \\n| wc -l)/3)) 6243 

Using bash interger builtin math processor

res=000$((($(find  . -type f -name '*.mp4' -printf "1+")0)*1000/3)) printf -v res "%.2f" ${res:0:${#res}-3}.${res:${#res}-3} echo $res 6243.33 

Pure bash

With recent 64bits bash, you could even use @glennjackman's ideas of using globstar, but computing pseudo floating could be done by:

shopt -s globstar files=(**/*.mp4) shopt -u globstar res=$[${#files[*]}000/3] printf -v res "%.2f" ${res:0:${#res}-3}.${res:${#res}-3} echo $res 6243.33 

There is no fork and $res contain a two digit rounded floating value.

Nota: Care about symlinks when using globstar and **!

Introducing shell connector

If you plan to do a lot of calculs, require high precision and use bash, you could use long running bc sub process:

mkfifo /tmp/mybcfifo exec 5> >(exec bc -l >/tmp/mybcfifo) exec 6</tmp/mybcfifo rm /tmp/mybcfifo 

then now:

echo >&5 '12/34' read -u 6 result echo $result .35294117647058823529 

This subprocess stay open and useable:

ps --sid $(ps ho sid $$) fw   PID TTY      STAT   TIME COMMAND 18027 pts/9    Ss     0:00 bash 18258 pts/9    S      0:00  \_ bc -l 18789 pts/9    R+     0:00  \_ ps --sid 18027 fw 

Computing $PI:

echo >&5 '4*a(1)' read -u 6 PI echo $PI 3.14159265358979323844 

To terminate sub process:

exec 6<&- exec 5>&- 

Little demo, about The best way to divide in bash using pipes!

Computing range {1..157} / 42 ( I will let you google for answer to the ultimate question of life, the universe, and everything ;)

... and print 13 result by lines in order to reduce output:

printf -v form "%s" "%5.3f "{,}{,}{,,};form+="%5.3f\n"; 

By regular way

testBc(){     for ((i=1; i<157; i++)) ;do         echo $(bc -l <<<"$i/42");     done } 

By using long running bc sub process:

testLongBc(){      mkfifo /tmp/mybcfifo;     exec 5> >(exec bc -l >/tmp/mybcfifo);     exec 6< /tmp/mybcfifo;     rm /tmp/mybcfifo;     for ((i=1; i<157; i++)) ;do         echo "$i/42" 1>&5;         read -u 6 result;         echo $result;     done;     exec 6>&-;     exec 5>&- } 

Let's see without:

time printf "$form" $(testBc) 0.024 0.048 0.071 0.095 0.119 0.143 0.167 0.190 0.214 0.238 0.262 0.286 0.310 0.333 0.357 0.381 0.405 0.429 0.452 0.476 0.500 0.524 0.548 0.571 0.595 0.619 0.643 0.667 0.690 0.714 0.738 0.762 0.786 0.810 0.833 0.857 0.881 0.905 0.929 0.952 0.976 1.000 1.024 1.048 1.071 1.095 1.119 1.143 1.167 1.190 1.214 1.238 1.262 1.286 1.310 1.333 1.357 1.381 1.405 1.429 1.452 1.476 1.500 1.524 1.548 1.571 1.595 1.619 1.643 1.667 1.690 1.714 1.738 1.762 1.786 1.810 1.833 1.857 1.881 1.905 1.929 1.952 1.976 2.000 2.024 2.048 2.071 2.095 2.119 2.143 2.167 2.190 2.214 2.238 2.262 2.286 2.310 2.333 2.357 2.381 2.405 2.429 2.452 2.476 2.500 2.524 2.548 2.571 2.595 2.619 2.643 2.667 2.690 2.714 2.738 2.762 2.786 2.810 2.833 2.857 2.881 2.905 2.929 2.952 2.976 3.000 3.024 3.048 3.071 3.095 3.119 3.143 3.167 3.190 3.214 3.238 3.262 3.286 3.310 3.333 3.357 3.381 3.405 3.429 3.452 3.476 3.500 3.524 3.548 3.571 3.595 3.619 3.643 3.667 3.690 3.714  real    0m10.113s user    0m0.900s sys     0m1.290s 

Wow! Ten seconds on my raspberry-pi!!

Then with:

time printf "$form" $(testLongBc) 0.024 0.048 0.071 0.095 0.119 0.143 0.167 0.190 0.214 0.238 0.262 0.286 0.310 0.333 0.357 0.381 0.405 0.429 0.452 0.476 0.500 0.524 0.548 0.571 0.595 0.619 0.643 0.667 0.690 0.714 0.738 0.762 0.786 0.810 0.833 0.857 0.881 0.905 0.929 0.952 0.976 1.000 1.024 1.048 1.071 1.095 1.119 1.143 1.167 1.190 1.214 1.238 1.262 1.286 1.310 1.333 1.357 1.381 1.405 1.429 1.452 1.476 1.500 1.524 1.548 1.571 1.595 1.619 1.643 1.667 1.690 1.714 1.738 1.762 1.786 1.810 1.833 1.857 1.881 1.905 1.929 1.952 1.976 2.000 2.024 2.048 2.071 2.095 2.119 2.143 2.167 2.190 2.214 2.238 2.262 2.286 2.310 2.333 2.357 2.381 2.405 2.429 2.452 2.476 2.500 2.524 2.548 2.571 2.595 2.619 2.643 2.667 2.690 2.714 2.738 2.762 2.786 2.810 2.833 2.857 2.881 2.905 2.929 2.952 2.976 3.000 3.024 3.048 3.071 3.095 3.119 3.143 3.167 3.190 3.214 3.238 3.262 3.286 3.310 3.333 3.357 3.381 3.405 3.429 3.452 3.476 3.500 3.524 3.548 3.571 3.595 3.619 3.643 3.667 3.690 3.714  real    0m0.670s user    0m0.190s sys     0m0.070s 

Less than one second!!

Hopefully, results are same, but execution time is very different!

My shell connector

I've published a connector function: Connector-bash on GitHub.com and shell_connector.sh on my own site.

source shell_connector.sh newConnector /usr/bin/bc -l 0 0 myBc 1764/42 result echo $result 42.00000000000000000000 
like image 42
F. Hauri Avatar answered Oct 11 '22 17:10

F. Hauri