Search code examples
linuxbashpipewc

bash script input from pipe and wc -m


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?


Solution

  • 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"