18

I have a simple bash function dividing two numbers:

echo "750/12.5" | bc

I'd like to take the output from bc and append /24 and pipe said result to another instance of bc.

Something like:

echo "750/12.5" | bc | echo $1 + "/24" | bc

Where $1 is the piped result.

P.S. I realize I could just do echo "750/12.5/24" | bc my question is more in regards to the appending of text to a pipe result.

4
  • 3
    echo $(echo "750/12.5" | bc )/24" | bc? – DopeGhoti Apr 22 '17 at 0:06
  • @DopeGhoti nice I hadn't considered that approach. – Philip Kirkbride Apr 22 '17 at 0:07
  • 1
    @DopeGhoti This should be an answer. It should be the accepted answer, that's the simplest and most natural way to do it. – Gilles 'SO- stop being evil' Apr 22 '17 at 21:38
  • 1
    @Gilles, thanks; that means something coming from you (: – DopeGhoti Apr 24 '17 at 1:55
10

In the simplest of the options, this does append to the pipe stream:

$ echo "750/12.5" | { bc; echo "/24"; }
60
/24

However that has an unexpected newline, to avoid that you need to either use tr:

$ echo "750/12.5" | { bc | tr -d '\n' ; echo "/24"; }
60/24

Or, given the fact that a command expansion removes trailing newlines:

$ printf '%s' $( echo "750/12.5" | bc ); echo "/24"
60/24

But probably, the correct way should be similar to:

$ echo "$(echo "750/12.5" | bc )/24"
60/24

Which, to be used in bc, could be written as this:

$ bc <<<"$(bc <<<"750/12.5")/24"
2

Which, to get a reasonable floating number precision should be something like:

$ bc <<<"scale=10;$(bc <<<"scale=5;750/12.5")/24"
2.5000000000

Note the need of two scale, as there are two instances of bc.

Of course, one instance of bc needs only one scale:

$ bc <<<"scale=5;750/12.5/24"

In fact, what you should be thinking about is in terms of an string:

$ a=$(echo "750/12.5")        # capture first string.
$ echo "$a/24" | bc           # extend the string
2

The comment about scale from above is still valid here.

2
  • 1
    Simple??? Why not a simple command substitution? – Gilles 'SO- stop being evil' Apr 22 '17 at 21:38
  • 1
    @Gilles A {…} is simple. The answer walks toward showing the user a "command substitution" as the best solution. Read again. – Isaac Apr 22 '17 at 23:52
19

I achieved this using sed and replacing the end of the line:

echo "my text" | sed 's/$/ more text/'

Returns:

my text more text

Your example:

echo "750/12.5" | bc | sed 's/$/\/24/' | bc
1
  • 2
    This doesn't work for multiline text input. In my case, I wanted to append an extra line to multiline text and process it. Ended up using: (figlet my_text && echo "new line") | boxes – Keith Ripley Jan 10 '20 at 18:32
5

Something like this seems to work using xargs:

$ echo "750/12.5" | bc |xargs -I{} echo "{}+20" |bc
80

Or even:

$ echo "750/12.5" | bc |echo "$(</dev/stdin)+20" |bc
80

And why not :

$ bc <<<20+$(bc <<<"750/12.5")
80
1
3

Alternative approach:

TL;DR: Use cat to concatenate with stdin (-) as an argument. Note that it'll newline-separate your concatenated streams; remove if undesired.

Unfortunately this approach is a bit cumbersome for your particular use case since bc fails if there's a newline in the middle of that expression:

$ echo "750/12.5" | bc | cat - <(echo "/24") | bc
60
(standard_in) 2: parse error

You can work around that by deleting the newline from the original bc result:

$ echo "750/12.5" | bc | tr -d '\n' | cat - <(echo "/24") | bc
2

However, I came upon this question by googling "bash append to pipe", and in my case I actually do want that newline between the concatenations. So for those here for the same reason, use | cat - <(...):

$ echo foo | cat - <(echo bar)
foo
bar
$ echo foo | cat - <(echo bar) | sort | cat - <(echo last_line)
bar
foo
last_line
$ echo foo | cat - <(echo bar) | sort | cat <(echo header) - <(echo last_line)
header
bar
foo
last_line

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.