Search code examples
bashperlterminalpipelinebc

When piping stdout into Perl, how to get it to print a newline character?


I just found out about piping stdout into Perl, and I was amazed that you could even do this:

[user@folder] $ echo print 1/3 | perl
0.33333[user@folder] $

From what I understand, you're putting the print command into Perl, and doing a floating point calculation using Perl's code. Please correct me. However, every time I do this, I get an answer that has no newline character. I've searched everywhere and I just can't keyword the terms specific enough to create the answer.

Here's a link to what I'm talking about, under poster Thor: How do I use floating-point division in bash?

He gave an amazing answer, but I couldn't comment or message the user, so I've decided to create a new question here.

I'm still trying to wrap my head around how this is working, variables as well.

$ three=3
$ echo $three/3 | perl
1[user@folder] $

Bonus question:
It started out as me just trying to get bash to output floating point numbers in arithmetic operations. I don't get why bc can't return a float. Supposedly it can, but it just isn't working for me.

Ideally:

$ echo 1/3 | bc
0
$

should return .333 and not 0. I'm delegating to a tool, bc, and bc is supposed to be able to do floats. I just don't understand what's going on.


Solution

  • Perl

    Use the -l option (see perldoc perlrun):

    $ echo print 1/3 | perl -l
    0.333333333333333
    $
    

    It adds newlines automatically. The documentation says:

    -l[octnum]

    enables automatic line-ending processing. It has two separate effects. First, it automatically chomps $/ (the input record separator) when used with -n or -p. Second, it assigns $\ (the output record separator) to have the value of octnum so that any print statements will have that separator added back on. If octnum is omitted, sets $\ to the current value of $/. For instance, to trim lines to 80 columns:

       perl -lpe 'substr($_, 80) = ""'
    

    Note that the assignment $\ = $/ is done when the switch is processed, so the input record separator can be different than the output record separator if the -l switch is followed by a -0 switch:

       gnufind / -print0 | perl -ln0e 'print "found $_" if -p'
    

    This sets $\ to newline and then sets $/ to the null character.


    Shell

    The shell expands shell variables, so there shouldn't be a surprise at this:

    $ three=3
    $ echo print $three/3 | perl -l
    1
    $ echo print $three/3
    print 3/3
    $
    

    bc

    For your bonus question:

    $ echo 1/3 | bc -l
    .33333333333333333333
    $
    

    It is pure fluke that the option is -l again. This time, it means 'load the library', and coincidentally sets the scale to 20, which is why there are 20 decimal digits shown.

    Incidentally, a quick way to get π to a large number of decimal places is:

    $ echo '4*a(1)' | bc -l
    3.14159265358979323844
    $ echo 'scale=40; 4*a(1)' | bc -l
    3.1415926535897932384626433832795028841968
    $
    

    The function a, loaded from the library by the -l option, is for 'arctan'. I observe that at least 3 sources from a Google search 'pi 40 digits' suggests that it should be: 3.1415926535 8979323846 2643383279 5028841971 — which suggests that there's an error of 3 counts in the 40th digit from bc.