Best way to divide in bash using pipes?

2020-05-23 04:03发布

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.

标签: linux bash math
4条回答
Root(大扎)
2楼-- · 2020-05-23 04:50

find . -name '*.mp4' | wc -l | xargs -I{} expr {} / 2

Best used if you have multiple outputs you'd like to pipe through xargs. Use{} as a placeholder for the expression term.

查看更多
地球回转人心会变
3楼-- · 2020-05-23 04:59

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 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

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 , 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
查看更多
SAY GOODBYE
4楼-- · 2020-05-23 05:06

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
查看更多
做自己的国王
5楼-- · 2020-05-23 05:06

Depending on your bash version, you don't even need find for this simple task:

shopt -s nullglob globstar
files=( **/*.mp4 )
dc -e "3 k ${#files[@]} 3 / p"

This method will correctly handle the bizarre edgecase of filenames containing newlines.

查看更多
登录 后发表回答