Mac(BSD 系) のwc とlinux(GNU 系) のwc は行数の数え方が違うのね

最近はlinux の勉強をしています。サーバ触ることになったからというのと、プログラミングの基礎知識が足りないと感じたためです。
プロセスとか、ストリームとか、その辺りの理解も曖昧だったしね。青木さんの「ふつうのLinux プログラミング」で勉強してます。超良い本。



この本の6章の練習問題に「wc -l」のように、ファイルの行数を数えるプログラムを書く課題があります。
例えば自分で最初に書いたのはこんなの。1文字ずつ読んで改行の数を数えるという超単純。

  • wc.c
#include <stdio.h>
#include <stdlib.h>

static int line_count();

int main(int argc, char *argv[]) {
  int result;
  result = line_count();
  printf("%d\n", result);
  exit(0);
}

static int line_count() {
  int counter = 0;
  int c;
  
  while((c = getchar()) != EOF) {
    if(c == '\n') counter++;
  }
  
  return counter;
}


1文字でなく行毎に読み込む方法をとるとline_count はこんな感じ。

#include <string.h>

# main は同じ・・・

#define BUFSIZE 1024

static int line_count() {
  char buf[BUFSIZE];
  int counter = 0;
  
  while(fgets(buf, sizeof buf, stdin)) {
    if((buf[(int)strlen(buf) - 1]) == '\n') counter++;
  }
  
  return counter;
}


実行結果はこんな感じ。

  • test.txt
test
hoge
$./wc < test.txt
2
$ wc -l test.txt
2


このtest.txt は2行目の最後に改行が入っています。この改行を外すと、結果は以下のようになります。

  • test.txt
test
hoge
$./wc < test.txt
1
$ wc -l test.txt
1


これは2行なんじゃないか?でも、オリジナルのwc とあってるからいいか・・・( ・ิω・ิ)
しかし、解答例を見て作り直してみると以下のような感じになりました。

  • wc.c
#include <stdio.h>
#include <stdlib.h>

static int line_count();

int main(int argc, char *argv[]) {
  int result;
  result = line_count();
  printf("%d\n", result);
  exit(0);
}

static int line_count() {
  int counter = 0;
  int c;
  char prev = '\n'; // for empty file
  
  while((c = getchar()) != EOF) {
    if(c == '\n') counter++;
    prev = c;
  }
  if(c != '\n') counter++; // for missing LF at the end of file
  
  return counter;
}


実行結果。

  • test.txt
test
hoge
$./wc < test.txt
2


はうぁ(⊃д⊂)


きちんと、最後の行に改行がない場合も1行とカウントするようになってる。Mac のwc はあくまで「行数=改行の数」の模様。

追記

id:zetamatta さんのブクマコメントにあった通り、GNU wc とBSD wc の違いでした。
試して見たところ、CentOS のwc もMac のwc と同じ結果になり、。Gentoo のwc では、改行がない最後の行もカウントしていました。
man を見たら、Mac CentOS のwc はBSDGentoo のはGNU でした。