4
1

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.

nlコマンドAdvent Calendar 2020

Day 18

nlコマンドで素数を求める

Last updated at Posted at 2020-12-17

今回はあまり「行番号」にこだわらずnlコマンドを使ってみましょう。

nlコマンドで整数を生成する

つまりseqコマンドの代替です。yesと組み合わせることで無限数列を生成できます。初期値は-v、増加値は-i、終了値はheadを組み合わせるとそれぞれ変更可能です。終了値は数列の個数で指定することになるので、seqよりも直感的に利用できる場面もあるかもしれません。

coreutils
$ seq 0 2 10
0
2
4
6
8
10
$ yes '' | nl -b a -s '' -w 1 -v 0 -i 2 | head -n 6 
0
2
4
6
8
10

これでほぼ等価になります。余計なスペースやタブを入れないように様々なオプションをつけていますが、必須なのは-b aのみです。BSD系のnlでは-wを必要な値の大きさに合わせて設定する必要があります。上記の場合でも-wを指定すると10が正しく表示されないので、余計なスペースが入ってしまいますが次のようにすべきです。除去が必要ならsedなどで加工しましょう。

BSD
$ yes '' | nl -b a -s '' -w 1 -v 0 -i 2 | head -n 6
0
2
4
6
8
0
$ yes '' | nl -n ln -b a -s '' -w 10 -v 0 -i 2 | head -n 6
0         
2         
4         
6         
8         
10        
$ yes '' | nl -n ln -b a -s '' -w 10 -v 0 -i 2 | head -n 6 | xxd
00000000: 3020 2020 2020 2020 2020 0a32 2020 2020  0         .2    
00000010: 2020 2020 200a 3420 2020 2020 2020 2020       .4         
00000020: 0a36 2020 2020 2020 2020 200a 3820 2020  .6         .8   
00000030: 2020 2020 2020 0a31 3020 2020 2020 2020        .10       
00000040: 200a                                      .

ちなみにGNU coreutilsのseqinfというキーワードを指定すれば、無限数列が生成できます。昔は終了値を設定しなければならないseqでは無限数列が生成できないと思っていたので、「それnlならできるよ」とかドヤ顔で言ってた時代もありました。

coreutils
$ seq inf | head -n 1000 | tail 
991
992
993
994
995
996
997
998
999
1000

ちなみにinfについてはManpageにもTexinfoにも書かれていないようです。こういう謎の機能に気づくみなさんは日常的にソースとか読んでるのかな?

素因数分解して素数を得る

nlの本題からはズレますが、factorコマンドを利用することで簡単に素因数分解した結果を得ることができます。区切られた数列データがれば、素因数分解した結果を得ることができます。

$ seq 5
1
2
3
4
5
$ seq 5 | factor
1:
2: 2
3: 3
4: 2 2
5: 5
$ echo {1..5}
1 2 3 4 5
$ echo {1..5} | factor
1:
2: 2
3: 3
4: 2 2
5: 5

この通りfactorは縦でも横でもいい感じに素因数分解してくれます。ここから素数だけを抽出しましょう。素数とは「1と自分自身以外に正の約数を持たない自然数」のことで、一般には含みません。2: 2のように、2つの列からなる行だけを対象に数値を抜き出せば良さそうです。awkでフィルタしてみましょう。

$ seq 5 | factor | awk 'NF==2&&$0=$2'
2
3
5

素数だけをフィルタすることができました。awkのスクリプトの内容ですが、フィールド数(列数NF)が2なら、行全体($0)に2列目($2)を代入という意味です。上記はアクションレス記法で書いていますが、以下と同じです。

$ seq 5 | factor | awk 'NF==2{print $2}'
2
3
5

日付のリストを作る

これはdateコマンドを使うのが間違いないと思います。以下のようにフォーマットを指定して、X日からN日後という日付を出力することができます。

$ date -d '20201201 +0days' +'%Y%m%d'
20201201
$ date -d '20201201 +30days' +'%Y%m%d'
20201231

つまり2020年12月の日付をすべて得るには、20201201を0日目として、30日後までを出力します。このdatexargsを使って、nlで生成した数列を差し込んで実行すれば良さそうです。-Iで指定した@の部分に入力データが挿入されます。

$ yes '' | nl -b a -v 0 | head -n 31 | xargs -I @ date -d '20201201 +@days' +'%Y%m%d'
20201201
20201202
20201203
(略)
20201231
20201231

しかし、あらかじめ月をまたがないことがわかっていれば、以下のようにsedで頭に年月を付加してもいいですね。

$ yes '' | nl -n rz -b a -w 2 | head -n 31 | sed 's/^/202012/'
20201201	
20201202	
(略)

あまり深堀っていくと、nlを使わずに・・・という話になってくるのでこの辺にしておきます。

$ echo 202012{01..31} 
20201201 20201202 (略)

素数日のリストを作る

では、つなげて実行してみましょう。

$ yes '' | nl -b a -v 0 | head -n 31 | xargs -I @ date -d '20201201 +@days' +'%Y%m%d' | factor | awk 'NF==2&&$0=$2'
20201227
20201231

今年の12月の素数日は12/27と12/31ということがわかりました。


seq infのTexinfo反映

(12/19追記)
seq infについてはTexinfoに使用法を記載してもらうことになりました。ちなみにsleep infもあるらしく、そちらも同時に反映されたようです。

余談

本当は素数日の記事として書こうかなとぼんやり考えていたのですが、残念ながら2020年のAdvent Calendarには素数日がありませんでした。しかし、なんと次回のシェル芸勉強会は12/27開催なので素数日です!

4
1
0

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
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?