5

I count the number of lines of my file with this command on OSX:

nl=$(wc -l < ~/myfile.txt)

Say, nl turns out to be 100. Now, I wish to use the result nl in another command, but weirdly,

echo 1-$nl

gives me 1- 100 instead of 1-100.

Demo

cv me$ nl=$(wc -l < ~/Desktop/cap.xlsx)
cv me$ echo $nl
104
cv me$ echo 1-$nl
1- 104

enter image description here

enter image description here

Why does this happen? How may I get 1-100?

7
  • What OS are you on? I can't reproduce this on my Debian.
    – terdon
    May 27, 2015 at 15:47
  • @terdon I am on OS X. Please see the updates. May 27, 2015 at 15:49
  • 1
    Weird. Make few tests to narrow the problem: 1) set value directly nl=100; 2) try different delimiter, not -; 3) put double quotes around expression echo "1-$nl"; 4) use printf instead of echo
    – jimmij
    May 27, 2015 at 16:03
  • @Sibbs; I either cannot reproduce it. What output do you get when quoting echo $nl as echo "$nl".
    – Janis
    May 27, 2015 at 16:03
  • @Janis I can't format that properly here. Please see the screenshot above. May 27, 2015 at 16:12

5 Answers 5

6

As POSIX defined, the output of wc shall contain an entry for each input file of the form:

"%d %d %d %s\n", <newlines>, <words>, <bytes>, <file>

But the output file format pseudo printf() string differs from the System V version of wc:

"%7d%7d%7d %s\n"

POSIX didn't require leading spaces to be added, so it's free for implementation to do what it want. There are different implementations of wc, at least with OSX and wc from heirloom tools chest, it added leading spaces to output.

$ /usr/5bin/wc -l /tmp/file
      3  /tmp/file

GNU wc also add leading spaces when reading from standard in and without any options:

$ cat file | wc
  5       5      65

To remove all leading spaces, in POSIX shell:

set -f
set -- $nl
nl=$1
set +f

Note that this approach assume that variable only contain leading or trailing spaces, no spaces in the middle, like a b.

0
4

wc implementations may produce leading spaces; this may also be depending on whether (and how many) options are used (GNU wc, for example, won't produce spaces if, as in your case, exactly one option is used). Those spaces are retained in the assignment nl=$(...). To remove the spaces, as a workaround, you could use either of:

nl=$(wc -l < ~/myfile.txt | awk '{print $1}')

or

nl=$(awk 'END{print NR}' ~/myfile.txt)

or

nl=$(wc -l < ~/Desktop/cap.xlsx)
nl=$(echo $nl)

or (but see @cuonglm's comment below for heirloom's version of wc)

nl=$(wc -l < ~/Desktop/cap.xlsx)
nl=${nl##* }
3
  • @cuonglm; Thanks for spotting that; I changed the shell regexp now from nl=${nl## *} to nl=${nl##* }.
    – Janis
    May 27, 2015 at 16:40
  • It still fail with heirloom wc, it produce trailing spaces, too. So you still remove the entire variable.
    – cuonglm
    May 27, 2015 at 16:42
  • The heirloom wc produces trailing spaces? - Okay. I'll leave that variant still as one option. With your comment here the folks now know that in heirloom context they should apply one of the other suggestions.
    – Janis
    May 27, 2015 at 16:46
4

Simpliest workaround (in bash or any other POSIX shell):

nl=$(wc -l < ~/myfile.txt)
nl=$(($nl))

$((...)) in standard sh is for arithmetic expansion, so the result will always be only the number itself. Note that if wc -l produces no output (like when the file can't be read), that will yield 0 instead of an empty string.

$((nl)) would also work in bash but is not guaranteed to by POSIX as $nl doesn't contain a numerical constant, and in practice doesn't work in yash for instance.

You can also do it all at once with:

nl=$(($(wc -l < ~/myfile.txt)))

Which has the benefit of preserving the exit status of wc.

0
1

As an alternative without the problem of spaces (POSIX):

$ sed -n '$=' ~/myfile.txt

Captured in a variable:

$ nl=$(sed -n '$=' ~/myfile.txt)
$ echo "1-$nl-"
1-100-

Note that sed counts the last line even if it doesn't end in a newline character (as it should in a properly formatted text file). More generally, sed may fail to print the number of newline characters in non-text files.


Another alternative:

$ nl=$(grep -v . ~/myfile.txt | tr '\n' 'x')
$ echo "1-${#nl}"
1-100

Note that grep will also print invalid characters (additionally to newlines) in the locale used.

2
  • @StéphaneChazelas Why is sed counts the last line even if it doesn't end in a newline character not enough for you? (hmmm, for reasonably clean text files)
    – user232326
    Sep 11, 2018 at 7:33
  • Sorry, I had overlooked that part. Sep 11, 2018 at 8:12
1

A simpler quick fix to remove leading and trailing spaces would be to cast it as integer.

echo 1-$nl

to

echo 1-$(($nl))

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .