labunix's blog

labunixのラボUnix

USP友の会 勉強会 番外編(OSC 2013 Tokyo/Spring)を解いてみた。

■USP友の会 勉強会 番外編(OSC 2013 Tokyo/Spring)を解いてみた。
 下記で番外編と、59までを解いてないと判明したようなので。。。

 「第13回危険でない方のシェル芸勉強会」を解いてみた。
 http://labunix.hateblo.jp/entry/20141005/1412439761

1.FizzBuzz(準備運動)

・こういう出力を得る

 1,2,Fizz,4,Buzz

・短く書くこと。

■短く無い方法

$ seq 1 16 | awk '{if($1%15==0){print "FizzBuzz"} \
              else{if($1%5==0) {print "Buzz"} \
              else{if($1%3==0) {print "Fizz"} \
              else{print $0}}}}' | xargs echo -n | sed s/" "/","/g
1,2,Fizz,4,Buzz,Fizz,7,8,Fizz,Buzz,11,Fizz,13,14,FizzBuzz,16

■短い方法
 括弧「()」が無くても良いみたいだけど変な癖が付かない程度に。

$ seq 1 16 | awk '($1%3==0){print "Fizz"}($1%5==0){print "Buzz"}{print $1}' | \
  tr '\n' ',';echo
1,2,Fizz,3,4,Buzz,5,Fizz,6,7,8,Fizz,9,Buzz,10,11,Fizz,12,13,14,Fizz,Buzz,15,16,

2.数を数える

■原稿って何?なので、bashのドキュメントから計測。
 sedを挟むのは同じ行に複数のキーワードがある想定。

$ zcat /usr/share/doc/bash/*.gz | sed s/"[Dd]ebian"/"&\n"/g | grep Debian | wc -l
30
$ zcat /usr/share/doc/bash/*.gz | sed s/"[Dd]ebian"/"&\n"/g | grep [Dd]ebian | wc -l
235

■同じ行に複数キーワードがあることを考慮しないと。。。

$ zcat /usr/share/doc/bash/*.gz | grep [Dd]ebian | wc -l
226

3.データ抽出

・キーa,bそれぞれについて、一番大きい数字を取り出してください。

$ cat hoge 
a 32
b 335
a 12
b -3.21
b 31533
a -301

■「uniq -w 1」は面白いオプション。

$ for c in a b;do grep "^${c}" hoge | sort -nr -k 2 | head -1;done
a 32
b 31533

$ sort -nr -k 1 -k 2 hoge | uniq -w 1 | head -2
b 31533
a 32

4.日付の計算

・1978216日は、
 2013223日の何日前でしょう?

$ echo $((`date -d "20130223" '+%s'` - `date -d "19780216" '+%s'`)) | awk '{print $1/(60*60*24)}'
12791

5.リストにないものを探す

・1から10までの数字のうち、numにないものは?

$ cat num 
7
6
4
2
10
1

$ seq 1 10 | cat - num | sort | uniq -c | awk '($1==1){print $2}'
3
5
8
9

6.エクセルの読み書き

■Tsukubaiのビジネス版のネタのようですので、
 debianで扱えるように変更。

$ sudo apt-get install -y python-openpyxl

$ cat sample_xlsx.py 
# -*- coding: utf-8 -*-
from openpyxl import Workbook
from openpyxl.cell import get_column_letter
wb = Workbook()
dest_filename = r'Excel2007_from_debian.xlsx'
ws = wb.worksheets[0]
ws.title = u"Unicode_日本語版"

ws.cell('A1').value = '項番'
ws.cell('A2').value = 1
ws.cell('A3').value = 2
ws.cell('A4').value = 3
ws.cell('A5').value = 4

ws.cell('B1').value = 'サンプル'
ws.cell('B2').value = ''
ws.cell('B3').value = ''
ws.cell('B4').value = ''
ws.cell('B5').value = ''

wb.save(filename = dest_filename)

$ python sample_xlsx.py

■上記で書いたものはLibreOfficeでも開ける。

$ libreoffice Excel2007_from_debian.xlsx

■コンソールで読みたい人向け

$ sudo apt-get install -y gnumeric
$ ssconvert Excel2007_from_debian.xlsx Excel2007_from_debian.csv
Using exporter Gnumeric_stf:stf_csv
$ cat Excel2007_from_debian.csv 
項番,サンプル
1,一
2,二
3,三
4,四

■csvならということで余談。

 年間カレンダーのcalとbash。CSV出力する。
 http://labunix.hateblo.jp/entry/20140120/1390148410

$ for y in `seq 1 12`;do \
    MYCAL="2014/${y}/01"; \
    echo -e "\n$MYCAL,,,,,," | sed s%"/01"%%; \
    echo ",,,,,,"; \
    n=`date -d "${MYCAL}" '+%u'`; \
    l=`date -d "${MYCAL} next month last day" '+%d'`; \
    if [ $n -ne 7 ];then \
      for s in `seq 1 $n`;do echo -n "0 ";done; \
    else \
      echo " "; \
    fi | \
    for d in `xargs` `seq 1 $l`;do \
      echo "$d $n" | \
        awk '{if(($1+$2)%7==0) printf "%3d\n",$1%32;else printf "%3d",$1%32}'; \
    done | sed s/" *\([0-9]*\)"/"\1,"/g | sed s/"^,"//g | sed s/",\$"//g | \
    sed s/"^,,"//g | sed s/"^0,"/","/g | sed s/",0"/","/g; \
    echo;unset n m MYCAL; \
  done > cal.csv

$ ssconvert --export-type=Gnumeric_Excel:xlsx cal.csv cal.xlsx
$ libreoffice cal.xlsx

■何も変更せずにxlsxからcsvにするとxlsxで見たと通りに年の表記がおかしくなる。

$ cat export_cal.csv | head -5
41640,,,,,,
日,月,火,水,木,金,土
,,,1,2,3,4
5,6,7,8,9,10,11
12,13,14,15,16,17,18

■年を書式設定で「YYYY/M」にすると「未定義の数の書式ID」が出るが問題ない。
 また、非表示の日が「/01」として出力される。

$ ssconvert cal.xlsx export_cal.csv
Using exporter Gnumeric_stf:stf_csv
未定義の数の書式ID '43'
未定義の数の書式ID '41'
未定義の数の書式ID '44'
未定義の数の書式ID '42'
Unexpected element 'workbookProtection' in state : 
	workbook
$ cat export_cal.csv 
2014/01/01,,,,,,
日,月,火,水,木,金,土
,,,1,2,3,4
5,6,7,8,9,10,11
12,13,14,15,16,17,18
19,20,21,22,23,24,25
26,27,28,29,30,31,
,,,,,,
2014/02/01,,,,,,
日,月,火,水,木,金,土
,,,,,,1
2,3,4,5,6,7,8
9,10,11,12,13,14,15
16,17,18,19,20,21,22
23,24,25,26,27,28,
,,,,,,
2014/03/01,,,,,,
日,月,火,水,木,金,土
,,,,,,1
2,3,4,5,6,7,8
9,10,11,12,13,14,15
16,17,18,19,20,21,22
23,24,25,26,27,28,29
30,31,,,,,
,,,,,,
2014/04/01,,,,,,
日,月,火,水,木,金,土
,,1,2,3,4,5
6,7,8,9,10,11,12
13,14,15,16,17,18,19
20,21,22,23,24,25,26
27,28,29,30,,,
,,,,,,
2014/05/01,,,,,,
日,月,火,水,木,金,土
,,,,1,2,3
4,5,6,7,8,9,10
11,12,13,14,15,16,17
18,19,20,21,22,23,24
25,26,27,28,29,30,31
,,,,,,
,,,,,,
2014/06/01,,,,,,
日,月,火,水,木,金,土
1,2,3,4,5,6,7
8,9,10,11,12,13,14
15,16,17,18,19,20,21
22,23,24,25,26,27,28
29,30,,,,,
,,,,,,
2014/07/01,,,,,,
日,月,火,水,木,金,土
,,1,2,3,4,5
6,7,8,9,10,11,12
13,14,15,16,17,18,19
20,21,22,23,24,25,26
27,28,29,30,31,,
,,,,,,
2014/08/01,,,,,,
日,月,火,水,木,金,土
,,,,,1,2
3,4,5,6,7,8,9
10,11,12,13,14,15,16
17,18,19,20,21,22,23
24,25,26,27,28,29,30
31,,,,,,
,,,,,,
2014/09/01,,,,,,
日,月,火,水,木,金,土
,1,2,3,4,5,6
7,8,9,10,11,12,13
14,15,16,17,18,19,20
21,22,23,24,25,26,27
28,29,30,,,,
,,,,,,
2014/10/01,,,,,,
日,月,火,水,木,金,土
,,,1,2,3,4
5,6,7,8,9,10,11
12,13,14,15,16,17,18
19,20,21,22,23,24,25
26,27,28,29,30,31,
,,,,,,
2014/11/01,,,,,,
日,月,火,水,木,金,土
,,,,,,1
2,3,4,5,6,7,8
9,10,11,12,13,14,15
16,17,18,19,20,21,22
23,24,25,26,27,28,29
30,,,,,,
,,,,,,
2014/12/01,,,,,,
日,月,火,水,木,金,土
,1,2,3,4,5,6
7,8,9,10,11,12,13
14,15,16,17,18,19,20
21,22,23,24,25,26,27
28,29,30,31,,,

■ここでは「/01」は唯一なので、「sed s%/01,%,%g」にパイプすれば消せる。

$ cat export_cal.csv | sed s%/01,%,%g | grep ^2014
2014/01,,,,,,
2014/02,,,,,,
2014/03,,,,,,
2014/04,,,,,,
2014/05,,,,,,
2014/06,,,,,,
2014/07,,,,,,
2014/08,,,,,,
2014/09,,,,,,
2014/10,,,,,,
2014/11,,,,,,
2014/12,,,,,,

7.速いソート
 1億行のデータのソート

■1億行のデータを作る方が大変。
 ところでTSUKUBAIのビジネス版のmsortは実在するlinuxコマンドとは別物です。

$ time seq 1 100000000 > sample.log

real	1m48.962s
user	1m44.307s
sys	0m2.956s

$ du -h sample.log 
849M	sample.log

8.ソートして集計

■これもTSUKUBAIコマンド入りなので、普通に。。。

$ time awk '{sum+=$1};END{print sum}' sample.log 
5000000050000000

real	1m3.529s
user	1m2.304s
sys	0m0.972s

■ガウスの等差数列の和の手法でごまかす。

$ time echo `handred_million=100000000;echo $((($handred_million/2)*($handred_million+1)))`
5000000050000000

real	0m0.002s
user	0m0.000s
sys	0m0.000s

9.足し算する(精度良く)

$ cat num 
0.124332364364363
-112532.2335
325235

$ cat num | tr '\n' '+' | echo `xargs`0 | bc
212702.890832364364363

10.横に並んだ数字のソート

$ cat file 
1 3153 131 31 41 9
31 0 3 245 123
3 42 53 1 19 4 124 1111

■一行づつ読み込んで行列入れ替え

$ while read line ;do \
    echo $line | tr ' ' '\n' | sort -n | xargs echo -n ;echo; \
  done <file
1 9 31 41 131 3153
0 3 31 123 245
1 3 4 19 42 53 124 1111