Reputation: 13
I have this code:
#!/bin/bash
# ~/test.sh
if [[ -p /dev/stdin ]]; then
var="-"
else
var="$1"
fi
l=$(cat -n "$var" | wc -l)
c=$(cat -n "$var" | wc -m)
echo l=$l
echo c=$c
If i run:
$ ./test.sh file.txt
l=73
c=4909 # it is correct
but if
cat file.txt | ./test.sh
l=73
c=0 #why i have 0???
i don't understand why i have c=0
cat file.txt | cat -n - | wc -c
works; so what's the problem with my code?
what am I doing wrong?
how can i make it work properly?
Upvotes: 1
Views: 610
Reputation: 19615
When you pipe rather than open a file, the difference is that the piped-in or stdin
is a stream (no seek back to start).
l=$(cat -n "$var" | wc -l)
Consumes the whole stream to count lines.
When it reaches:
c=$(cat -n "$var" | wc -m)
The stdin
stream has all been consumed by the previous counting of lines. It has nothing to count left, thus returning 0
.
Fortunately wc
can do both on the same run, and then you can read the result values like this:
#!/usr/bin/env bash
var=${1--}
if [ -p /dev/stdin ]; then
if [ $# -ge 1 ]; then
printf %s\\n 'Need either stdin or filename argument (not both)!' >&2
exit 1
fi
elif [ $# -ne 1 ]; then
printf %s\\n 'Need stdin or a single filename argument!' >&2
exit 1
fi
read -r l c _ < <(wc -lm -- "$var")
printf 'l=%d\nc=%d\n' "$l" "$c"
Upvotes: 2
Reputation: 189628
The first cat
consumes standard input until end of file; there is then no way to read the same data again for the second cat
.
The usual solution to this is to use a temporary file; but if you just want the different counts from wc
, run it once and extract them both from its output.
set -- $(cat -n "${@--}" | wc -l -m)
l=$1
c=$2
Notice also how the parameter expansion "${@--}"
expands to either "$@"
or -
depending on whether the script received arguments.
Upvotes: 1