3

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?

  • What OS are you on? I can't reproduce this on my Debian. – terdon May 27 '15 at 15:47
  • @terdon I am on OS X. Please see the updates. – Sibbs Gambling May 27 '15 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 '15 at 16:03
  • @Sibbs; I either cannot reproduce it. What output do you get when quoting echo $nl as echo "$nl". – Janis May 27 '15 at 16:03
  • @Janis I can't format that properly here. Please see the screenshot above. – Sibbs Gambling May 27 '15 at 16:12
4

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.

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##* }
  • @cuonglm; Thanks for spotting that; I changed the shell regexp now from nl=${nl## *} to nl=${nl##* }. – Janis May 27 '15 at 16:40
  • It still fail with heirloom wc, it produce trailing spaces, too. So you still remove the entire variable. – cuonglm May 27 '15 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 '15 at 16:46
1

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.

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.

  • @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) – Isaac Sep 11 '18 at 7:33
  • Sorry, I had overlooked that part. – Stéphane Chazelas Sep 11 '18 at 8:12

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.