Best way to divide in bash using pipes?

后端 未结 4 1602

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
<         


        
相关标签:
4条回答
  • 2021-02-02 14:18

    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
    
    0 讨论(0)
  • 2021-02-02 14:21

    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.

    0 讨论(0)
  • 2021-02-02 14:32

    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.

    0 讨论(0)
  • 2021-02-02 14:33

    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
    
    0 讨论(0)
提交回复
热议问题