2

How can I run a command from a bash script and at end print the number of lines it printed?

The awk equivalent would be

{ print; } 
END { print NR; }

I could only come up with the following. This works for now, but I can not get it outside the subshell.

| (lc=0; while read; do echo $REPLY; lc=$(($lc+1)); done; echo $lc)

UPDATE

Seems my problem was something quite different. I had first tried to use /dev/stdout which failed:

tester> { echo abc | tee /dev/stdout | wc -l;  } > /tmp/abc; cat /tmp/abc
2

This seemed to be because tee /dev/stdout gives error:

tester> echo abc | tee /dev/stdout
tee: /dev/stdout: Permission denied
abc

The answer by Jeff Schaller worked:

tester> { echo abc | tee >(wc -l);  } > /tmp/abc; cat /tmp/abc
abc
1

But this morning on retrying I saw it works!

tmp> echo abc | tee /dev/stdout
abc
abc

After spending some time I found two problems:

When |tee /dev/stdout failed yesterday I had logged in as usr1 done a sudo to another usr2, sudo su usr2. In this case the chain of soft link is /dev/stdout -> /proc/self/fd/1 -> /dev/pts/1 and the last one was owned by usr1. So the tee failed. Today I tried as usr1, so it succeeded.

But then I realized the other error with tee /dev/stdout, which is that all output goes to wc -l; line count is double and nothing is printed.

  • I tried messing around with tee to no avail, but there's this ugliness: do_stuff > output; (cat output; wc -l output; rm -f output). – DopeGhoti May 16 '17 at 23:22
  • Do you need to just print the number of lines? Then why not just pipe the output to that awk you already have? Or do you need to do something with the data while it's being output? What do you want to do and does it have to be in shell? Or, do you want to have the number lines for use later in the shell script? As you can tell, I'm not exactly sure what "I can not get it outside the subshell" refers to. – ilkkachu May 17 '17 at 1:42
  • Yes I would like to read it into a bash variable if possible. But my root problem was something else. See the update. – Miserable Variable May 17 '17 at 19:25
2

I'm not sure how the line-count could be printed any better:

$ seq 1 5 2>&1 | tee >(wc -l | { read lines; echo Lines of output: $lines; })
1
2
3
4
5
$ Lines of output: 5

This redirects the command's (here seq 1 5) stderr to stdout, so that tee sees both of those streams; tee then copies its input back out and also copies the input to a process substitution that runs wc -l to count the lines. I then read the output of wc into the $lines variable to provide a report.

  • Thanks Jeff. Process Substitution is interesting. Did not know about it. – Miserable Variable May 17 '17 at 19:24
2

Using sed:

some_command | sed -n 'p;$='

For example:

$ touch file.{a..k}
$ printf '%s\n' * | sed -n 'p;$='
file.a
file.b
file.c
file.d
file.e
file.f
file.g
file.h
file.i
file.j
file.k
11

The sed script will just copy the input to the output with the print command (p) applied to every line of input, but for the last line ($), it will also print the line number (=).

  • Simpler to say: sed '$=' ...? – Jeff Schaller May 18 '17 at 12:01
  • 1
    @JeffSchaller That's what I thought at first too, but that outputs the line number of the last line ahead of the line itself. – Kusalananda May 18 '17 at 12:06
1

You can simply use your awk command :

echo -e "foo\nbar"| awk '{ print; }END { print NR; }'
foo
bar
2
  • Yes I can :) As I wrote in my question (but not very clearly) I want to assign it to a variable – Miserable Variable May 17 '17 at 19:19

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.