Linux系:manコマンド使ってみよう!
私は、普段の開発はほぼLinuxで行っています。
その際に、「コマンドのオプションなんだっけ?」とか、「関数の引数なんだっけ?」と思うことがあります。
インターネットで調べるのも有りですが、ぱっと調べられるので使えるようになっていると便利です。
manコマンドでmanを調べる
manコマンドでmanについても調べることができます。
$ man man
以下はページャで出力されます。
MAN(1) マニュアルページユーティリティー MAN(1) 名前 man - an interface to the system reference manuals 書式 man [man options] [[section] page ...] ... man -k [apropos options] regexp ... man -K [man options] [section] term ... man -f [whatis options] page ... man -l [man options] file ... man -w|-W [man options] page ... 説明 man is the system's manual pager. Each page argument given to man is normally the name of a program, utility or function. The manual page associated with each of these arguments is then found and displayed. A section, if provided, will direct man to look only in that section of the manual. The default action is to search in all of the available sections following a pre-defined order (see DEFAULTS), and to show only the first page found, even if page exists in several sections. 次の表はマニュアルの section 番号およびその section に含まれるページの種類を示します。 1 実行プログラムまたはシェルコマンド 2 システムコール (カーネルが提供する関数) 3 ライブラリー呼び出し (プログラムライブラリーに含まれる関数) 4 Special files (usually found in /dev) 5 File formats and conventions, e.g. /etc/passwd 6 ゲーム 7 Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7) 8 システム管理コマンド (通常は root 用) 9 カーネルルーチン [非標準] マニュアルページ page は複数の節で構成されます。 :
私はセクション1,2,3をよく見ます。セクション1を見ていると、よく使っているコマンドでも知らないオプションについて記述があったり、別の機会に使えそうなオプションの記述があったりと発見があります。
調べたい内容が出力されない場合
例えばですが、printfについて調べようとするとセクション1のprintfコマンドの内容が出力されます。
$ man printf
PRINTF(1) User Commands PRINTF(1) NAME printf - format and print data SYNOPSIS printf FORMAT [ARGUMENT]... printf OPTION DESCRIPTION Print ARGUMENT(s) according to FORMAT, or execute according to OPTION: :
printfコマンドについて調べる場合はこれでよいのですが、printf関数について調べたい場合はライブラリーのセクション3を指定して調べます。
$ man 3 printf
PRINTF(3) Linux Programmer's Manual PRINTF(3) NAME printf, fprintf, dprintf, sprintf, snprintf, vprintf, vfprintf, vdprintf, vsprintf, vsnprintf - formatted output conversion SYNOPSIS #include <stdio.h> int printf(const char *format, ...); int fprintf(FILE *stream, const char *format, ...); int dprintf(int fd, const char *format, ...); int sprintf(char *str, const char *format, ...); int snprintf(char *str, size_t size, const char *format, ...); :
どのセクションにあるかよくわからない場合は-kオプションが助けになると思います。
$ man -k printf Printf (3o) - Formatted output functions. asprintf (3) - print to allocated string dprintf (3) - formatted output conversion fprintf (3) - formatted output conversion fwprintf (3) - formatted wide-character output conversion printf (1) - format and print data printf (3) - formatted output conversion set_matchpathcon_printf (3) - set flags controlling the operation of matchpathcon or matchpathcon_inde... snprintf (3) - formatted output conversion sprintf (3) - formatted output conversion Stdlib.Printf (3o) - no description swprintf (3) - formatted wide-character output conversion vasprintf (3) - print to allocated string vdprintf (3) - formatted output conversion vfprintf (3) - formatted output conversion vfwprintf (3) - formatted wide-character output conversion vprintf (3) - formatted output conversion vsnprintf (3) - formatted output conversion vsprintf (3) - formatted output conversion vswprintf (3) - formatted wide-character output conversion vwprintf (3) - formatted wide-character output conversion wprintf (3) - formatted wide-character output conversion XtAsprintf (3) - memory management functions
shell:天気情報をWebAPIで取得してみる
Twitter APIを使ってみようと思ったのですが、認証やら何やらが必要になりそうでした。これまでWebAPIは使ったことがなかったので、ひつまず手に届きそうな天気情報を取得するものから試してみました。
お天気Webサービス仕様
手に届きそうなものを探していたらちょうど「お天気Webサービス仕様」(http://weather.livedoor.com/weather_hacks/webservice)というものを見つけました。
横浜の天気情報をリクエストする
説明を読むと同ページからリンクしている「全国の地点定義表(RSS)」に記載されている一次細分区のidを使って天気情報をリクエストできるようでした。
試しに、横浜(140010)を使ってリクエストを投げてみます。
リクエストを送信する
$ GET http://weather.livedoor.com/forecast/webservice/json/v1?city=140010 {"pinpointLocations":[{"link":"http://weather.livedoor.com/area/forecast/1410000","name":"\ :
JSONデータから必要情報を得る
取得した情報から自分が欲しい情報を抽出します。
欲しい情報は予報(forecasts)の以下です。
dateLabel
date
telop
jqコマンドでの抽出
$ GET http://weather.livedoor.com/forecast/webservice/json/v1?city=140010 | jq -r '.forecasts | .[] | [ .dateLabel, .date, .telop]' [ "今日", "2020-06-11", "雨" ] [ "明日", "2020-06-12", "曇のち雨" ] [ "明後日", "2020-06-13", "曇時々雨" ]
整形
好みの形に整形します。
$ GET http://weather.livedoor.com/forecast/webservice/json/v1?city=140010 | jq -r '.forecasts | .[] | [ .dateLabel, .date, .telop] | @sh' | sed s/\'//g 今日 2020-06-11 雨 明日 2020-06-12 曇のち雨 明後日 2020-06-13 曇時々雨
スクリプト化(weather.sh)
#!/bin/bash #----------------------------------------- # 天気情報取得スクリプト #----------------------------------------- if [ $# -eq 0 -o _"$1" = _"-h" ]; then echo Usage : $(basename $0) 一次細分区ID echo 例 東京 : $(basename $0) 130010 echo 例 横浜 : $(basename $0) 140010 exit 1; fi city=$1 url=http://weather.livedoor.com/forecast/webservice/json/v1?city=${city} #----------------------------------------- # 一次細分区で指定した天気情報を得る。 #----------------------------------------- GET ${url} | #----------------------------------------- # JSONから欲しい情報を得る。 #----------------------------------------- jq -r ' #----------------------------- # 予報部分の抽出 #----------------------------- .forecasts | #----------------------------- # 配列の全要素を抽出(今日・明日・明後日) #----------------------------- .[] | #----------------------------- # 必要情報を抽出 #----------------------------- [ .dateLabel, .date, .telop] | @sh' | #----------------------------------------- # クォート(')を除外する。 #----------------------------------------- sed s/\'//g
実行
$ ./weather.sh 140010
今日 2020-06-11 雨
明日 2020-06-12 曇のち雨
明後日 2020-06-13 曇時々雨
Prolog:3x3の魔法陣の解を求める
gprologのFDの練習として、SPINで以前に書いた3x3の魔法陣の解を求めてみます。gprologのソースファイルにはサンプルが含まれています。その中にmagsq.plがあり、魔法陣のサイズを指定できるものがあります。
ソース ms.pl
/* 魔法陣 */ ms(MS) :- MS = [ V11, V12, V13, V21, V22, V23, V31, V32, V33 ], /* 各要素は1〜9である。*/ fd_domain(MS, 1, 9), /* 各要素の値は異なる。*/ fd_all_different(MS), /* 行の和は15である。*/ V11 + V12 + V13 #= 15, V21 + V22 + V23 #= 15, V31 + V32 + V33 #= 15, /* 列の和は15である。*/ V11 + V21 + V31 #= 15, V12 + V22 + V32 #= 15, V13 + V23 + V33 #= 15, /* 対角の和は15である。*/ V11 + V22 + V33 #= 15, V31 + V22 + V13 #= 15, fd_labeling(MS). /* 出力用の述語 */ pp([V11, V12, V13, V21, V22, V23, V31, V32, V33]) :- write(V11), write(' '), write(V12), write(' '), write(V13), nl, write(V21), write(' '), write(V22), write(' '), write(V23), nl, write(V31), write(' '), write(V32), write(' '), write(V33), nl.
実行
$ gprolog GNU Prolog 1.4.5 (64 bits) Compiled Mar 17 2020, 23:55:22 with gcc By Daniel Diaz Copyright (C) 1999-2020 Daniel Diaz | ?- ['ms.pl']. : | ?- ms(Ans),pp(Ans). 2 7 6 9 5 1 4 3 8 Ans = [2,7,6,9,5,1,4,3,8] ? ; 2 9 4 7 5 3 6 1 8 Ans = [2,9,4,7,5,3,6,1,8] ? ; 4 3 8 9 5 1 2 7 6 Ans = [4,3,8,9,5,1,2,7,6] ? ; 4 9 2 3 5 7 8 1 6 Ans = [4,9,2,3,5,7,8,1,6] ? ; 6 1 8 7 5 3 2 9 4 Ans = [6,1,8,7,5,3,2,9,4] ? ; 6 7 2 1 5 9 8 3 4 Ans = [6,7,2,1,5,9,8,3,4] ? ; 8 1 6 3 5 7 4 9 2 Ans = [8,1,6,3,5,7,4,9,2] ? ; 8 3 4 1 5 9 6 7 2 Ans = [8,3,4,1,5,9,6,7,2] ? ; (1 ms) no
Prolog:1〜9の2つの整数で和が10になる組み合わせを求める
gprologのマニュアルを見ていると「9 Finite domain solver and built-in predicates」という章がありました。「9.1 introduction」にも「constraint solver」という記述があるので、何らかの制約問題を解いてくれるのかと思い試して見ました。
簡単そうなサンプルとして、以下のような問題を考えてみました。
- A,B が1〜9の範囲の整数をとる。
- A+Bが10となる組み合わせを考える。
- A>=Bとする。
gplorgの起動
$ gprolog GNU Prolog 1.4.5 (64 bits) Compiled Mar 17 2020, 23:55:22 with gcc By Daniel Diaz Copyright (C) 1999-2020 Daniel Diaz | ?-
1〜9の範囲のA,Bを用意
| ?- fd_domain([A,B],1,9). A = _#0(1..9) B = _#17(1..9) yes
それぞれに値を代入してみる
| ?- fd_domain([A,B],1,9), fd_labeling([A,B]). A = 1 B = 1 ? ; A = 1 B = 2 ? ; A = 1 B = 3 ? yes
組み合わせがどんどん出てきます。上記の例では止めていますが、A=9,B=9の組み合わせまで出てきます。
A+Bが10となる制約
| ?- fd_domain([A,B],1,9), A + B #= 10, fd_labeling([A,B]). A = 1 B = 9 ? a A = 2 B = 8 A = 3 B = 7 A = 4 B = 6 A = 5 B = 5 A = 6 B = 4 A = 7 B = 3 A = 8 B = 2 A = 9 B = 1 (1 ms) yes
A + B = 10 となる組み合わせが出てきますが、3+7=10, 7+3=10 のような組み合わせを省くため、大小関係の制約を追加します。
大小関係の制約 A >= Bの追加
| ?- fd_domain([A,B],1,9), A + B #= 10, A #>= B, fd_labeling([A,B]). A = 5 B = 5 ? ; A = 6 B = 4 ? ; A = 7 B = 3 ? ; A = 8 B = 2 ? ; A = 9 B = 1 (1 ms) yes
Prolog:アローダイアグラム クリティカルパスを求める問題(基本情報処理技術者試験)を解いてみる
基本情報技術者試験で出てくるアローダイヤグラムでクリティカルパスを求める問題ですが、prologを使うとスッキリかけるのではないかと思い挑戦してみました。使用したprologの処理系はgprologとなります。
問題は以下のようなものでした。
問題の情報をprologで表現したものが以下となります。
ブランチ情報
% ブランチ情報 % branchInfo(ブランチ名, 元ノード, 先ノード, コスト). branchInfo(a, n1, n2, 1). branchInfo(b, n1, n3, 5). branchInfo(c, n2, n3, 3). branchInfo(d, n2, n4, 3). branchInfo(e, n3, n5, 4). branchInfo(dmy, n5, n4, 0). branchInfo(f, n5, n6, 5). branchInfo(g, n4, n6, 4).
ブランチ情報からパス情報を得る述語を次のように定義しました。
パス
% パス %path(元ノード, 先ノード, [ブランチ情報]). path(StartNode, EndNode, [branchInfo(PathName, StartNode, EndNode, Weight)]) :- branchInfo(PathName, StartNode, EndNode, Weight). path(StartNode, EndNode, [branchInfo(PathName, StartNode, NextStart, Weight)|T]) :- branchInfo(PathName, StartNode, NextStart, Weight), path(NextStart, EndNode, T).
これでStartNodeからEndNodeに至るブランチのリストが得られます。
例としてn1からn3のパスを確認してみます。
パスの確認(n1->n3)
| ?- path(n1,n3,P). P = [branchInfo(b,n1,n3,5)] ? ; P = [branchInfo(a,n1,n2,1),branchInfo(c,n2,n3,3)] ? ; no
続いてブランチの名前とコストを集める述語を定義しました。
% pathCost(元ノード, 先ノード, result(パス, コスト)). pathCost(StartNode, EndNode, result(Rout, Cost)) :- % パスを得る。 path(StartNode, EndNode, R), % パスの経路名を集める。 maplist(branchName, R, Rout), % パスのコストを得る。 maplist(weight, R, Ws), sum_list(Ws, Cost). branchName(branchInfo(Name,_,_,_), Name). weight(branchInfo(_, _, _, Weight), Weight).
prologならではなのですが、pathCost(n1,n6,R)で問い合わせて「;」や「a」でn1からn6に至る各ルートのコストが得られるのでほぼ、答えは得ているような感じになりました。
パスのコスト(n1->n6)
| ?- pathCost(n1,n6,R). R = result([a,c,e,f],13) ? ; R = result([a,c,e,dmy,g],12) ? a R = result([a,d,g],8) R = result([b,e,f],14) R = result([b,e,dmy,g],13) no
クリティカルパスを求める述語を定義します。
クリティカルパス
% クリティカルパスを得る。 criticalPath(StartNode, EndNode, CriticalPath) :- % 各ルートとコスト情報を得る。 findall(D,pathCost(StartNode,EndNode,D),Ds), % 最大コストの場所を得る。 maplist(cost,Ds,Cs), max_list(Cs,Max), nth(Pos,Cs,Max), % クリティカルパスの情報を得る。 nth(Pos,Ds,CriticalPath). cost(result(_,C), C).
クリティカルパス(n1->n6)
クリティカルパスを問い合わせてみます。
| ?- criticalPath(n1,n6,Cp). Cp = result([b,e,f],14) ? ; no
一応全体は以下のようになります。
コード全体
% ブランチ情報 % branchInfo(ブランチ名, 元ノード, 先ノード, コスト). branchInfo(a, n1, n2, 1). branchInfo(b, n1, n3, 5). branchInfo(c, n2, n3, 3). branchInfo(d, n2, n4, 3). branchInfo(e, n3, n5, 4). branchInfo(dmy, n5, n4, 0). branchInfo(f, n5, n6, 5). branchInfo(g, n4, n6, 4). % パス %path(元ノード, 先ノード, [パス情報]). path(StartNode, EndNode, [branchInfo(PathName, StartNode, EndNode, Weight)]) :- branchInfo(PathName, StartNode, EndNode, Weight). path(StartNode, EndNode, [branchInfo(PathName, StartNode, NextStart, Weight)|T]) :- branchInfo(PathName, StartNode, NextStart, Weight), path(NextStart, EndNode, T). % pathCost(元ノード, 先ノード, result([ブランチ名], コスト)). pathCost(StartNode, EndNode, result(BranchNames, Cost)) :- % ルートを得る。 path(StartNode, EndNode, R), % ルートの経路名を集める。 maplist(branchName, R, BranchNames), % ルートのコストを得る。 maplist(weight, R, Ws), sum_list(Ws, Cost). branchName(branchInfo(Name,_,_,_), Name). weight(branchInfo(_, _, _, Weight), Weight). % クリティカルパスを得る。 criticalPath(StartNode, EndNode, CriticalPath) :- % 各ルートとコスト情報を得る。 findall(D,pathCost(StartNode,EndNode,D),Ds), % 最大コストの場所を得る。 maplist(cost,Ds,Cs), max_list(Cs,Max), nth(Pos,Cs,Max), % クリティカルパスの情報を得る。 nth(Pos,Ds,CriticalPath). cost(result(_,C), C).
SPIN:1信号(赤青黃)の遷移サンプル
「SPINによる設計モデル検証」のNever Claimの2信号サンプルがあるのですが、自身の勉強としてもっと簡単なサンプルから作成してみました。モデルとしては、赤から青になり、青から黄、黄から赤になる信号のモデルと、Never Claimを記述します。
記述したファイルは以下となります。
signal3.pml
/* 信号の色 */ mtype = { BLUE, YELLOW, RED } mtype light = RED; active proctype signal() { do :: atomic { light == RED -> if :: light = BLUE; :: light = RED; fi } :: atomic { light == BLUE -> if :: light = YELLOW; :: light = BLUE; /* バグを仕込むと検出される。*/ //:: light = RED; fi } :: atomic { light == YELLOW -> if :: light = RED :: light = YELLOW; fi } od } never n01 { /* 信号:赤からの遷移 */ LIGHT_RED: if :: light == RED -> goto LIGHT_RED :: light == BLUE -> goto LIGHT_BLUE :: else -> goto accept fi /* 信号:青からの遷移 */ LIGHT_BLUE: if :: light == BLUE -> goto LIGHT_BLUE :: light == YELLOW -> goto LIGHT_YELLOW :: else -> goto accept fi /* 信号:黃からの遷移 */ LIGHT_YELLOW: if :: light == YELLOW -> goto LIGHT_YELLOW :: light == RED -> goto LIGHT_RED :: else -> goto accept fi accept: skip; goto accept }
検証器の作成と実行
$ spin -a -run -a signal3.pml warning: for p.o. reduction to be valid the never claim must be stutter-invariant (never claims generated from LTL formulae are stutter-invariant) (Spin Version 6.5.0 -- 17 July 2019) + Partial Order Reduction Full statespace search for: never claim + (n01) assertion violations + (if within scope of claim) acceptance cycles + (fairness disabled) invalid end states - (disabled by never claim) State-vector 20 byte, depth reached 11, errors: 0 6 states, stored 7 states, matched 13 transitions (= stored+matched) 6 atomic steps hash conflicts: 0 (resolved) Stats on memory usage (in Megabytes): 0.000 equivalent memory usage for states (stored*(State-vector + overhead)) 0.287 actual memory usage for states 128.000 memory used for hash table (-w24) 0.534 memory used for DFS stack (-m10000) 128.730 total actual memory usage unreached in proctype signal signal3.pml:32, state 22, "-end-" (1 of 22 states) unreached in claim n01 signal3.pml:63, state 27, "-end-" (1 of 27 states) pan: elapsed time 0 seconds
バグを仕込む
signal3.pmlのコメントアウトしている「//:: light = RED;」をコメント解除すると、エラーが検出されてきます。
$ spin -a -run -a signal3.pml warning: for p.o. reduction to be valid the never claim must be stutter-invariant (never claims generated from LTL formulae are stutter-invariant) pan:1: acceptance cycle (at depth 12) pan: wrote signal3.pml.trail (Spin Version 6.5.0 -- 17 July 2019) Warning: Search not completed + Partial Order Reduction Full statespace search for: never claim + (n01) assertion violations + (if within scope of claim) acceptance cycles + (fairness disabled) invalid end states - (disabled by never claim) State-vector 20 byte, depth reached 20, errors: 1 10 states, stored 6 states, matched 16 transitions (= stored+matched) 10 atomic steps hash conflicts: 0 (resolved) Stats on memory usage (in Megabytes): 0.000 equivalent memory usage for states (stored*(State-vector + overhead)) 0.286 actual memory usage for states 128.000 memory used for hash table (-w24) 0.534 memory used for DFS stack (-m10000) 128.730 total actual memory usage pan: elapsed time 0 seconds
trailの確認
信号が赤(初期値)から青、赤と遷移した際にNever claimのaccetpにジャンプすることになり、最終的にエラーとして検出されています。
$ spin -t -p -g signal3.pml starting claim 1 Never claim moves to line 39 [((light==RED))] 2: proc 0 (signal:1) signal3.pml:9 (state 1) [((light==RED))] 3: proc 0 (signal:1) signal3.pml:11 (state 2) [light = BLUE] light = BLUE Never claim moves to line 40 [((light==BLUE))] 5: proc 0 (signal:1) signal3.pml:16 (state 7) [((light==BLUE))] 6: proc 0 (signal:1) signal3.pml:19 (state 9) [light = BLUE] Never claim moves to line 47 [((light==BLUE))] 8: proc 0 (signal:1) signal3.pml:16 (state 7) [((light==BLUE))] 9: proc 0 (signal:1) signal3.pml:21 (state 10) [light = RED] light = RED Never claim moves to line 49 [else] 11: proc 0 (signal:1) signal3.pml:9 (state 1) [((light==RED))] 12: proc 0 (signal:1) signal3.pml:11 (state 2) [light = BLUE] light = BLUE <<<<<START OF CYCLE>>>>> Never claim moves to line 61 [(1)] 14: proc 0 (signal:1) signal3.pml:16 (state 7) [((light==BLUE))] 15: proc 0 (signal:1) signal3.pml:18 (state 8) [light = YELLOW] light = YELLOW 17: proc 0 (signal:1) signal3.pml:25 (state 14) [((light==YELLOW))] 18: proc 0 (signal:1) signal3.pml:27 (state 15) [light = RED] light = RED 20: proc 0 (signal:1) signal3.pml:9 (state 1) [((light==RED))] 21: proc 0 (signal:1) signal3.pml:11 (state 2) [light = BLUE] light = BLUE spin: trail ends after 21 steps #processes: 1 light = BLUE 21: proc 0 (signal:1) signal3.pml:7 (state 20) 21: proc - (n01:1) signal3.pml:61 (state 25) 1 processes created
GCC:関数重複定義(Multiple Definition)を-z muldefsで許可する
同僚の方が一時的な確認のために同名の関数を静的リンクした実行ファイルを作成しようとしてエラーになっていました。C言語ではできないんじゃないかな。と思っていたのですが、「HPではできたのに〜」と言っていたのを聞き、ならばGCCでもオプションでどうにかなるのではと思って調べてみました。
結果、リンクオプション -z muldefs を利用することで可能でした。
使用したファイルは以下となります。
func01.c
#include "func.h" int func(int x) { return x + 10; }
func02.c
#include "func.h" int func(int x) { return x + 100; }
main.c
#include <stdio.h> #include "func.h" int main(void) { int x = func(10); printf("func(10) = %d\n", x); return 0; }
通常のコンパイル
普通にコンパイルしてみます。
$ gcc -Wall main.c func01.c func02.c /tmp/ccKwhV9A.o: 関数 `func' 内: func02.c:(.text+0x0): `func' が重複して定義されています /tmp/ccGuOvnL.o:func01.c:(.text+0x0): ここで最初に定義されています collect2: error: ld returned 1 exit status
funcが重複してリンクが失敗しています。
マニュアルにあたる
ldのマニュアルを見ると -zオプションのキーワードを指定すると重複定義を許可してくれそうです。(ld --helpでも見つけられました。)
$ man ld : -z keyword The recognized keywords are: : muldefs Allow multiple definitions.
リンクオプション -z muldefsを試してみる
リンク時のエラーが解消されています。また、今回試した例では、左側の定義が有効になっているようです。
$ gcc -Wall -z muldefs main.c func01.c func02.c $ ./a.out func(10) = 20 $ gcc -Wall -z muldefs main.c func02.c func01.c $ ./a.out func(10) = 110