47

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.

5
  • 3
    echo $(echo "750/12.5" | bc )/24" | bc? Commented Apr 22, 2017 at 0:06
  • @DopeGhoti nice I hadn't considered that approach. Commented Apr 22, 2017 at 0:07
  • 2
    @DopeGhoti This should be an answer. It should be the accepted answer, that's the simplest and most natural way to do it. Commented Apr 22, 2017 at 21:38
  • 1
    @Gilles, thanks; that means something coming from you (: Commented Apr 24, 2017 at 1:55
  • worth noticing vipe as an interactive on-the-pipe vi session. Commented May 28, 2023 at 22:55

6 Answers 6

40

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 
2
  • 7
    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 Commented Jan 10, 2020 at 18:32
  • Hugo, you're a command line ninja w/this! Awesome man, this is great! Commented Jul 20, 2022 at 21:09
24

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 
3
  • This is the (elegant and) correct answer. Commented Mar 27, 2023 at 22:42
  • How do you append with a content of a file? Commented Jun 27, 2023 at 11:30
  • instead of the first : echo "first part..." (which adds a newline), use printf "%s" "first part...." (or printf "first part..." if there are no % in it) which will not put a newline, and thus makes appending the following of the line of computation easier) Commented Sep 21, 2023 at 9:49
19

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? Commented Apr 22, 2017 at 21:38
  • 2
    @Gilles A {…} is simple. The answer walks toward showing the user a "command substitution" as the best solution. Read again. Commented Apr 22, 2017 at 23:52
6

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
  • 1
    Why not? Because it's unnecessarily complicated. Use a command substitution. Commented Apr 22, 2017 at 21:39
2

You could use awk to insert/append anything to the pipe:

$ date | awk 'BEGIN{print "prefix line"} {print} END{print "suffix line"}' prefix line Mo 21. Aug 15:24:30 CEST 2023 suffix line 

where

  • date - is your input pipe (current date for example here)
  • BEGIN{print "prefix line"} - this block will insert output before main
  • {print} - prints input pipe's contents; one could do some processing here if needed
  • END{print "suffix line"} - this appends output after main

To do same in single line, just use printf:

$ date | awk 'BEGIN{printf("prefix line >> ")} {printf($0)} END{printf(" << suffix line")}' prefix line >> Mo 21. Aug 15:32:08 CEST 2023 << suffix line/upload/sc505enb/tmp> $ echo "750/12.5" | bc | awk '{printf($0)} END{printf("/24\n")}' 60/24 
0
-1

another possible BASH way - create a helper function which processes the stdin with read command and then prints extra line(s):

$ function append_lines(){ local data; while read -r data; do echo -en "$data"; done; echo "$*"; } $ date | append_lines " +suffice_;line1" " ++line2" " +++line_3" [Mon Aug 21 16:00:37 CEST 2023][ +suffice_;line1 ++line2 +++line_3] $ echo "750/12.5" | bc | append_lines "/24" | bc 2 

where

  • while read -r data; do echo -en "$data"; done; - just passes the stdin (pipe) through. remove the -en modifier to print separate input lines.
  • echo "$*"; - prints all arguments of append_lines() to stdout
1
  • 1
    would be nice to know why this solution was downgraded? Commented Nov 2, 2023 at 11:28

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.