8

I'm writing a small script that needs to run a program that outputs multiple lines, and then display the count of those lines. The program, however, can take several seconds to run and I would rather not run it twice, once for the output and another for the count.

I can do it running the program twice:

#!/bin/bash
count=$(program-command | wc -l)
program-command
printf "$count lines"

Is there a way to get the count and output while only running the program once? This output has formatting, so ideally that formatting (colors) would be preserved.

2
  • I know it's naive but, why not saving the output and then counting the lines? Something like program-command > output and then wc -l output Commented Apr 15, 2016 at 16:13
  • @Pie86 naive solutions can be good! I was trying to do something like that, but I was failing. I'll try using > as you suggested, thanks. Commented Apr 15, 2016 at 16:18

4 Answers 4

19

Use tee and process substitution:

program-command | tee >(wc -l)

To preserve color, prefix the command with script -q /dev/null as per this answer:

script -q /dev/null program-command | tee >(wc -l)
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks @Ben. It looks like this technically works, but loses the color formatting. Any ideas for that?
Is it possible to store the count in a bash variable(so that it can be used subsequently) without redirecting the output to a file and finding out the value in last line. somekind of magic with BASH_REMATCH?
The braces around wc -l suggest a child shell is invoked so any value from wc -l can not be passed to avariable of parent shell..so how to store the value in a variable in parent shell.
After some hit and trial i could get the following to work. function __RESULT { export LINE_COUNT=$(cat -);}; exec 3>&1; __RESULT < <( { cat 1.sh | tee >(wc -l 1>&2); } 2>&1 >&3 3>&-;) 3>&- ; exec 3>&-; unset -f __RESULT; echo $LINE_COUNT
5

You could use awk:

program-command | awk '{print $0; count++} END {print count}'

2 Comments

this is technically working, but I've lost the formatting from the command. The original output is colorized, but this produces uncolored text. Any ideas on that?
I guess this is related to program-command that outputs colors only when then output is passed to tty. For example, if program-command would be ls, you should use it like this: ls --color=always | awk '{print $0; count++} END {print count}' (this way the colors will be preserved)
0

There are many ways to do something like that. My personal preference is to use a file on /tmp since I mount it in memory. This way you can write to the file and then count line and output it very quickly.

If you do no have access to a memory mounted filesystem, try using an array to hold the results so you can use the array size echo ${#ArrayName[@]} and then output it echo ${arrName[@]}.

1 Comment

thanks for the response. I realized one of my goals is to preserve the color formatting of the original output. Would these solutions do that?
0

After taking some insights from Ben's answer above I could get following to work:

$# > function getOutputAndLineCount {
       function __RESULT { export LINE_COUNT=$(cat -); }
       exec 3>&1
       __RESULT < <( { cat $1 | tee >(wc -l 1>&2); } 2>&1 >&3 3>&-;) 3>&-
       exec 3>&-
       unset -f __RESULT;
     }
$#> getOutputAndLineCount 1.sh
a
b
c
$#> echo $LINE_COUNT
3

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.