0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ふつうのLinuxプログラミングをやってみる その7

Last updated at Posted at 2020-11-10

有名な本らしいので買ってみました
ふつうのLinuxプログラミング Linuxの仕組みから学べるgccプログラミングの王道
ふつうのLinuxプログラミング 第2版 Linuxの仕組みから学べるgccプログラミングの王道

ふつうのLinuxプログラミングをやってみる その1
https://qiita.com/uturned0/items/b9ae846f2aff5865c074
ふつうのLinuxプログラミングをやってみる その2
https://qiita.com/uturned0/items/56beac990cdd6f1059ed
ふつうのLinuxプログラミングをやってみる その3
https://qiita.com/uturned0/items/675092da8aa89c4b1ff0
その4
https://qiita.com/uturned0/items/8f5765cfc0f0be8a1981
その5
https://qiita.com/uturned0/items/ab97deb489c994a836da
その6
https://qiita.com/uturned0/items/b7df48e87ae9170f3698
その7
https://qiita.com/uturned0/items/263151cf7c83bff7fac1
その7−3
https://qiita.com/uturned0/items/3fbf3ed6bb2a47325f59


chapter 7

headコマンドを作ります

# include <stdio.h>
# include <stdlib.h>

static void do_head(FILE *f, long nlines);

int
main(int argc, char *argv[])
{
	if (argc != 2) {
	    fprintf(stderr, "Usage: %s n\n", argv[0]);
		exit(1);
	}
	do_head(stdin, atol(argv[1]));
	exit(0);
}

static void
do_head(FILE *f, long nlines)
{
	int c;

	if (nlines <= 0) return;
	while ((c=getc(f)) != EOF) {
		if (putchar(c) < 0) exit(1);
		if (c == '\n') {
		    nlines--;
    		if (nlines == 0) return;
		}
	}
}

execute

$ gcc -Wall head.c && cat head.c | ./a.out 2
# include <stdio.h>
# include <stdlib.h>

動いた!

atol()

atol, atoll, atol_l, atoll_l -- convert ASCII string to long or long long integer
long
atol(const char *str);

文字列をlongの数値にする。キャストってこと? 試してみた

# include <stdio.h>
# include <stdlib.h>

int
main(int argc, char *argv[])
{
    char *str = "123";
    printf("%s \n",str);

    // printf("%d",str);    // これはエラーになる

    // 返り値がlongなので %ld じゃないとエラーになる
    printf("%ld \n", atol(str));

    // long 変数に入れてみる
    unsigned long n = atol(str);
    printf("%ld \n", n);

    // printfの %d はエラーだったが、int 変数に入れることはできる・・んだ・・・
    unsigned int i = atol(str);
    printf("%d \n", i);

}

execute

$ gcc -Wall atol.c && ./a.out 
123 
123 
123 
123 

キャストしてるってことだった。longの場合は %ld にしないとprintfできない。 %d だとエラー。でも、int n = はできるんだなぁ。コンパイラが許すってことは使えるシーンがあるんだろうか

%ld は今初めて知ったけど、コンパイラのエラーが素敵で。

image.png

ぐぐりもせずに %ld ってわかったね。素敵だ.

atoi() strtoll() strtol() strtod()

文字を数値にcastする系

fgets() を使わない理由

gets()のほうがバッファないからいい。困ることがない
fgets()は行長の制限があるので向いてない

number を n と省略する

nlines = number of lines

Eiffelという言語の真似らしい。Cだとよくあるのかも

Eiffel は Bertrand Meyer 氏が設計した強い型付きの (生まれつきの) オブジェクト指向言語です。
http://guppy.eng.kagawa-u.ac.jp/~kagawa/publication/eiffel.pdf

fileを引数で渡す

少し時間が空いたのでだいたい忘れたけど、だいたい覚えてた。fopen()ね。

# include <stdio.h>
# include <stdlib.h>

static void do_head(FILE *f, long nlines);

int
main(int argc, char *argv[])
{
    long nlines;
    int i ;
	if (argc < 2) {
	    fprintf(stderr, "Usage: %s n\n", argv[0]);
		exit(1);
	}

    nlines = atol(argv[1]);

    if (argc == 2) {
		do_head(stdin, nlines);
    } else {
    	for (i=2; i<argc; i++) {
    	    FILE *f;
    	    f = fopen(argv[i], "r");
    	    if (!f) {
    	        perror(argv[i]);
    	        exit(1);
            }
            do_head(f, nlines);
            fclose(f);
    	}
    }
	exit(0);
}

static void
do_head(FILE *f, long nlines)
{
	int c;

	if (nlines <= 0) return;
	while ((c=getc(f)) != EOF) {
		if (putchar(c) < 0) exit(1);
		if (c == '\n') {
		    nlines--;
    		if (nlines == 0) return;
		}
	}
}


reulst

$ gcc -Wall head2.c && ./a.out 3 txt .git/config
abc
xyz

[core]
        repositoryformatversion = 0
        filemode = true

single/double quote が混乱する

これをsingle quoteで 'r' にするとエラーなのね

    	    f = fopen(argv[i], "r");

image.png

1文字だから singleだと思ったんだが・・ "rb" とか打てるからchar型じゃないとだめなのか。たぶん。

linux commandのオプションとは

パラメータありなし
ls -a
head -n 10

long option / short option
head --line
head -n

「それロングオプションにしようよ」とか言ったことないので、覚えておこう。ロングオプション好きなもので。

long optionのあとに = が必要かはコマンドによるよね。めんどいところ。headは関係ないんだって。

head --line 5
head --line=5

- 単体は 「stdinから入力せよ」
-- 単体は 「ここでオプション解析を停止せよ」 マイナスから始まる文字列を引数として渡したいときなどに使われる。

ほう。

-- はあまり使わないのでよくわからんな。一番使うのは git checkout -- index.html (変更しちゃったindex.htmlをもとに戻す) ですな。 -- の後ろの引数であるファイル名に - があり得るからってことなのかもしれない。そうだったのか。なるほどそうやってパースする決まり事を作ればよかったのか。なるほど

getopt()

ショートオプション解析用の関数。ふるーい。UNIXからあーる。すごい。

あー。これbash scriptで見たことあるね。やっぱり。manには getopt(1) と getopt(3) があるわ。

GETOPT (1) BSD General Commands Manual GETOPT(1)
getopt -- parse command options
GETOPT (3) BSD Library Functions Manual GETOPT(3)
getopt -- get option character from command line argument list

while + switch caseでoptionごとに処理を決めていける。よく見そうなので覚えたほうがいいんだろうけど、ざっくりとだけ読んだ。

パラメータありなしで動きがちがって
例えば head -n 5 ならgetoptが持つグローバル変数はこうなる

optopt = n // ハイフンがなくなってるのに注意
optarg = 5 // これがパラメータ
optind = 2 // argvのindex

そうか、今更だけど key=value じゃなくて option=parameter って感じの言葉定義になるのか。値はパラメータ。そうなのか。

--- で解析をやめるけど、この2つそれぞれ optind の場所が変わるから注意。 - はとどまる。 -- は次の引数に動く。

getopt_long(3)

 int
 getopt_long(int argc, char * const *argv, const char *optstring, const struct option *longopts, int *longindex);

ロングオプションも解析できる! おお〜〜これ知りたかったやつ。これはコマンドはないのね。GNU libcにだけあるらしい。

むう。気が散って今日はだめだ。寝YO

起きた。少しやる。

続き読むのに「メンバ」という言葉がわからなかったので学ぶ

構造体の場合と同様、クラスの構成要素は、メンバと呼ばれます。これには、メンバ変数とメンバ関数とがありますが、普通の変数宣言や、関数プロトタイプの書き方と同じです。ここで宣言されるので、外側にプロトタイプ宣言を書く必要はありません。
https://cpp-lang.sevendays-study.com/day2.html#:~:text=%E3%83%A1%E3%83%B3%E3%83%90%E5%A4%89%E6%95%B0%E3%83%BB%E3%83%A1%E3%83%B3%E3%83%90%E9%96%A2%E6%95%B0,%E3%82%92%E6%9B%B8%E3%81%8F%E5%BF%85%E8%A6%81%E3%81%AF%E3%81%82%E3%82%8A%E3%81%BE%E3%81%9B%E3%82%93%E3%80%82

クラス変数とクラスメソッド、くらいの気持ちでおこうか。

--help & -h を関連付けたければ flags = NULL, val = 'h' にすると、 --helpがきたときに getopt(3)の h を呼ぶ、的な感じらしい。素敵だ

真偽値(1 or 0)をとるオプションは、そのまま変数に反映できる。

int opt_all = 0;

struct option[] = {
{"--all", no_argument, &opt_all, 1},

opt_allがデフォルトゼロで、--allが来たら opt_allを1にするってことかな。 & ってなんだっけ。

&var 変数自身のアドレスを表示
https://qiita.com/pen2000/items/fc5b06795dce3e0f679c

ポインタだった。*で宣言して&で使うんだったかも。 opt_allがある場所に1を入れる的な。C-lang大変だなぁ・・

--lines と -n が使えるheadコマンド

# include <stdio.h>
# include <stdlib.h>
# define _GNU_SOURCE
# include <getopt.h>    // プロトタイプ宣言をGNU SOURCEから読み込む

static void do_head(FILE *f, long nlines);

# define DEFAULT_N_LINES 10

static struct option longopts[] = {
    {"lines", required_argument, NULL, 'n'},
    {"help", no_argument, NULL, 'h'},
    {0, 0, 0, 0}   // <-------- 最後の配列だよ、というマーキング。決まりぽい
};

int
main(int argc, char *argv[])
{
    int opt;
    long nlines = DEFAULT_N_LINES;

    while ((opt = getopt_long(argc, argv, "n:", longopts, NULL)) != -1) {
        switch (opt) {
            case 'n':
                nlines = atoi(optarg); //行数の数字が optargに入ってくる
                break;
            case 'h':
                fprintf(stdout, "Usage: %s [-n LINES] [FILE ...]\n", argv[0]);
                exit(0);
            case '?':
                // ここはエラー処理なのでstderr似にだす。内容は getopt_longが勝手に出してくれる!
                fprintf(stderr, "Usage: %s [-n LINES] [FILE ...]\n", argv[0]);
                exit(1);
        }
    }

    if (optind == argc) {
		do_head(stdin, nlines);
    } else {
        int i;

    	for (i=optind; i < argc; i++) {
    	    FILE *f;

    	    f = fopen(argv[i], "r");
    	    if (!f) {
    	        perror(argv[i]);
    	        exit(1);
            }
            do_head(f, nlines);
            fclose(f);
    	}
    }
	exit(0);
}

static void
do_head(FILE *f, long nlines)
{
	int c;

	if (nlines <= 0) return;
	while ((c=getc(f)) != EOF) {
		if (putchar(c) < 0) exit(1);
		if (c == '\n') {
		    nlines--;
    		if (nlines == 0) return;
		}
	}
}

result

@local:~/repos/futu$  gcc -Wall head3.c && ./a.out -n 3 txt
0a
1b
2c
@local:~/repos/futu$  gcc -Wall head3.c && ./a.out --lines 3 txt
0a
1b
2c
@local:~/repos/futu$ 

うごいた。

配列って [] じゃなくて {} なんだったか

この n: ってなんだっけ。

getopt_long(argc, argv, "n:", longopts, NULL)

const char *optdecl これは option decleration かな。解析するオプションを羅列する。 -a, -t, -x なら tax でもいい。パラメータを取る引数の場合は後ろに : をつける。xがvalue必要なら、 x: になる。他のと混ぜるときも順番関係なくて、あくまで x: がくっついて塊になってればいい。 tax: とか x:ta とか。

今回使うオプションは↓

  • -n
  • --lines
  • -h
  • --help

ここで指定するのはショートプションだけ。そうすると h と、 n じゃなくて n: になる、nはパラメータを取るから。 -n 10 で10行分ね。でも第3引数は hn: ではなくて n: だけ。この理由は理解できなかった。「解析したいショートオプションだけ書きます」とあるけど解析とは? switch case 'h' は解析ではない? 不明。

    if (optind == argc) {

ここに来る時点でoptind(intなargv配列のindex)は定義されたオプションはすべて処理されている。個々に入る引数はオプション以外の普通の引数。 head --lines 10 a.txt b.txt の a.txtとb.txtの部分。あとはこの引数を回して処理する。

GPU libc を使う注意点

getopt_long内でおそらくgetopt()を使ってて、それはGNC libcから引っ張ってる。C言語はどのOSでも動くけど、GNC libcを使ったものはLinux限定の動きをしてしまう。GNU windowsなんてないもんな
GNU libcを使った場合はlinuxだけで使えると認識するように。

gdb をつかう!!!!!

キターー!!!知りたかったーーー!!!


この本を読みながらやってます

ふつうのLinuxプログラミング Linuxの仕組みから学べるgccプログラミングの王道
ふつうのLinuxプログラミング 第2版 Linuxの仕組みから学べるgccプログラミングの王道

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?