シェル操作課題をPythonでやってみた

はてブのマイホットエントリーを眺めてたらこんなものを見つけた。
シェル操作課題 (cut, sort, uniq などで集計を行う) 設問編 - Yamashiro0217の日記
awkとか全然知らないから勉強になるかなと思って覗いてみたんだけど…

いっそpythonワンライナーで実行したらカッコイイと思うw

やりましょう

Python3.2でやってみたよ

問1 このファイルを表示しろ
$ python3 -c "import functools,operator;print(functools.reduce(operator.__add__, (open('hoge.csv','r').readlines())))"

server1,1343363124,30,/video.php
server2,1343363110,20,/profile.php
server3,1343363115,7,/login.php
server1,1343363105,8,/profile.php
server2,1343363205,35,/profile.php
server2,1343363110,20,/profile.php
server3,1343363205,30,/login.php
server4,1343363225,12,/video.php
server1,1343363265,7,/video.php

問2 このファイルからサーバー名とアクセス先だけ表示しろ
$ python3 -c "import csv,operator,functools;print(functools.reduce(operator.__add__, [','.join((r[0], r[3]+'\n')) for r in csv.reader(open('hoge.csv','r'))]))"

server1,/video.php
server2,/profile.php
server3,/login.php
server1,/profile.php
server2,/profile.php
server2,/profile.php
server3,/login.php
server4,/video.php
server1,/video.php

問3 このファイルからserver4の行だけ表示しろ
$ python3 -c "print([line for line in open('hoge.csv','r').readlines() if line[6]=='4'][0])"

server4,1343363225,12,/video.php

問4 このファイルの行数を表示しろ
$ python3 -c "print(len(open('hoge.csv','r').readlines()))"

9

問5 このファイルをサーバー名、ユーザーIDの昇順で5行だけ表示しろ
$ python3 -c "import operator,csv,functools;print(functools.reduce(operator.__add__, [','.join((r[0],str(r[1]),str(r[2]),r[3])) for r in sorted([(r[0], int(r[1]), int(r[2]), r[3]+'\n') for r in csv.reader(open('hoge.csv','r'))], key=operator.itemgetter(0,2))][:5]))"

server1,1343363265,7,/video.php
server1,1343363105,8,/profile.php
server1,1343363124,30,/video.php
server2,1343363110,20,/profile.php
server2,1343363110,20,/profile.php

これが一番ひどい。数値としてソートするためにintに変換して、その後文字列連結するためにintにしたものをstrに直してる。

問6 このファイルには重複行がある。重複行はまとめて数え行数を表示しろ
$ python3 -c "print(len(set(open('hoge.csv','r'))))"

8

問7 このログのUU(ユニークユーザー)数を表示しろ
$ python3 -c "import csv;print(len(set([r[2] for r in csv.reader(open('hoge.csv','r'))])))"

6

問8 このログのアクセス先ごとにアクセス数を数え上位1つを表示しろ
$ python3 -c "import csv,collections;print(collections.Counter([r[1] for r in set([(r[2],r[3]) for r in csv.reader(open('hoge.csv','r'))])]).most_common(1))"

[('/video.php', 3)]

表示がちょっとアレだけど、知りたい情報が取り出せてるしこれでいいよねもう。

問9 このログのserverという文字列をxxxという文字列に変え、サーバー毎のアクセス数を表示しろ
$ python3 -c "import csv,collections;print(collections.Counter(map(lambda x: x.replace('server', 'xxx'), [r[0] for r in csv.reader(open('hoge.csv','r'))])))"

Counter({'xxx1': 3, 'xxx2': 3, 'xxx3': 2, 'xxx4': 1})

これまた表示が形式が違うけどこれでいいよね。ていうか出題者の想定結果が間違ってる気がするんですが。

問10 このログのユーザーIDが10以上の人のユニークなユーザーIDをユーザーIDでソートして表示しろ
$ python3 -c "import csv;print(set([r[2] for r in csv.reader(open('hoge.csv','r')) if int(r[2]) >= 10]))"

{'12', '30', '20', '35'}

これまた表示が(ry

感想

Pythonワンライナーなんてやるもんじゃない。

Python2.7のunittestのassertRaisesはコンテキストマネージャーとして使用出来る

テスト対象

def add(x, y):
    return x + y

適当すぎだけどご勘弁。異なった型同士を足そうとすれば当然TypeError吐きますよね。

本題

Python2.7以前のunittestのassertRaisesは assertRaises(exception, callback, *args, **kwargs) というように呼び出す必要があった。

class SomeTest(unittest.TestCase):
    def test_add(self):
        # 正しい書き方
        self.assertRaises(TypeError, add, (2, [1, 2, 3]))
        # 間違った書き方
        self.assertRaises(TypeError, add(2, [1, 2, 3]))

このテストを実行すると間違った書き方のところでassertRaisesがTypeErrorを捕らえきれずに普通にテストが落ちます。でも、見た目的に間違った書き方のほうが感覚的にはしっくりするよなーっていうのが本音。

Python2.7以降のassertRaisesはコンテキストマネージャーとして使える

で、python2.7からはassertRaisesがコンテキストマネージャーとして使えるよう変更が加えられた。これにより、より直感的なこのような書き方が可能になった。

class SomeTest(unittest.TestCase):
    def test_add(self):
        with self.assertRaises(TypeError):
            add(2, [1, 2, 3])

実にスッキリしていてわかりやすい。まさにPythonicってやつですね。

Grubのデフォルト起動OSを変更する

あまり使わなくなったWin/Ubuntuデュアルブートマシンを同居人が使いたいということで、デフォルト起動OSをWindowsになるようにしなくてはいけなくなった。で、いちおうぐぐってみたらすぐ見つかったんだけど、同じくらいのタイミングでこんなアドバイスをいただいた。

ぐぐって見つけた記事はmenu.lstを編集すると書いてたけど、どうもそういうファイルはなくて、knzm2011さんの言うとおりに

$ sudo grub-update

して /boot/grub/grub.cfg を編集した。このファイルのはじめの方に

set default="0"

と書かれている行があるのだけど、この数値を変更すればいいみたい。OS選択画面にて Windows 7 は上から5番目だったので

set default="4"

として再起動したら、OS選択画面にて無事 windows 7 の行がデフォルトで選択されている状態になった。めでたしめでたし。