最近ちょこちょこPythonを触る機会が多くなってきたのだが、処理の中でgrepやawkのように行の抽出をさせたいことがある。 Subprocessでgrepとかawkを呼び出すのはかっこ悪いし、Python内で処理を完結させたいというのもあったので少し調べてみた。

1.指定した文字列を含む行を抽出

1-1.基本的な抽出方法

Pythonでgrepのような処理を行うには、find('文字列')を用いてその文字列を含む数を指定することで抽出が可能だ。 以下、記述例。

# -*- coding: utf-8 -*
import sys
ld = open(sys.argv[1])
lines = ld.readlines()
ld.close()

for line in lines:
    if line.find("文字列") >= 0:
        print line[:-1]
[root@BS-PUB-CENT7-01 ~]# cat ./test.py
# -*- coding: utf-8 -*
import sys
ld = open(sys.argv[1])
lines = ld.readlines()
ld.close()

for line in lines:
    if line.find("aaa") >= 0:
        print line[:-1]
[root@BS-PUB-CENT7-01 ~]# cat /tmp/test.txt
aaa 111
bbb 222
ccc 333
ddd 444
eee 555
fff 666
ggg 777
hhh 888
iii 999
jjj 111
[root@BS-PUB-CENT7-01 ~]# python ./test.py /tmp/test.txt
aaa 111

1-2. AND検索・OR検索を行う

AND検索・OR検索を行う場合は、ifのとこでand/orの指定をしてやればよい。

# -*- coding: utf-8 -*
import sys
ld = open(sys.argv[1])
lines = ld.readlines()
ld.close()

for line in lines:
    if line.find("文字列1") >= 0 and line.find("文字列2") >= 0:
        print line[:-1]
for line in lines:
    if line.find("文字列1") >= 0 or line.find("文字列2") >= 0:
        print line[:-1]
[root@BS-PUB-CENT7-01 ~]# cat ./test.py
# -*- coding: utf-8 -*
import sys
ld = open(sys.argv[1])
lines = ld.readlines()
ld.close()

print 'and'
for line in lines:
    if line.find("aaa") >= 0 and line.find("111") >= 0:
        print line[:-1]

print 'or'
for line in lines:
    if line.find("aaa") >= 0 or line.find("bbb") >= 0:
        print line[:-1]
[root@BS-PUB-CENT7-01 ~]# python ./test.py /tmp/test.txt
and
aaa 111
or
aaa 111
bbb 222

1-3. 大文字・小文字を区別せずに検索する

pythonのfind()では大文字・小文字を区別して抽出を行ってくれる。 それを区別せずに検索をするといった場合は、とりあえず大文字・小文字のどっちかに変換してやればよい。

# -*- coding: utf-8 -*
import sys
ld = open(sys.argv[1])
lines = ld.readlines()
ld.close()

for line in lines:
    if line.upper().find("文字列(大文字)") >= 0:
        print line[:-1]
[root@BS-PUB-CENT7-01 ~]# cat ./test.py
# -*- coding: utf-8 -*
import sys
ld = open(sys.argv[1])
lines = ld.readlines()
ld.close()

for line in lines:
    if line.upper().find("BBB") >= 0:
        print line[:-1]
[root@BS-PUB-CENT7-01 ~]# python ./test.py /tmp/test.txt
bbb 222

2. 行の先頭・末尾が指定した文字列の行を抽出

行の先頭や末尾が指定した文字列の行を抽出する場合、文頭の場合は「startswith('文字列')」、末尾の場合は「endswith('文字列')」を利用することで抽出が行える。 なお、endswithについては行が改行を含める場合はそれも指定してやる必要があるので注意。 (もしくは「replace('置換前文字列','置換後文字列',回数)」を前に挟んで、改行を削除してしまうとよいだろう)

# -*- coding: utf-8 -*
import sys
ld = open(sys.argv[1])
lines = ld.readlines()
ld.close()

print 'startswith'
for line in lines:
    if line.startswith("b"):
        print line[:-1]

print 'endswith'
for line in lines:
    if line.endswith("1\n"):
        print line[:-1]

[root@BS-PUB-CENT7-01 ~]# cat ./test.py
# -*- coding: utf-8 -*
import sys
ld = open(sys.argv[1])
lines = ld.readlines()
ld.close()

print 'startswith'
for line in lines:
    if line.startswith("b"):
        print line[:-1]

print 'endswith'
for line in lines:
    if line.endswith("1\n"):
        print line[:-1]
[root@BS-PUB-CENT7-01 ~]# python ./test.py /tmp/test.txt
startswith
bbb 222
endswith
aaa 111
jjj 111

3. 指定した列の値や行の何文字目の値に応じて行を抽出

3-1. 指定した列の値に応じて行を抽出する

awkのように指定した列の値に応じて抽出をする場合は、split()で分割してやるのが手っ取り早い。 列だけを抽出したければ普通に「print 配列名[列数 -1](以下の例だと、2列名の出力はprint column[1])」で行える。

# -*- coding: utf-8 -*
import sys
ld = open(sys.argv[1])
lines = ld.readlines()
ld.close()

for line in lines:
    column = line.strip().split()
    if column[1] == "111":
        print line[:-1]

3-2. 文字の位置に応じて行を抽出する

文字の位置に応じて行を抽出する場合、find()やrfind()startswith()、endswith()で開始位置や終了位置などを指定することで対応できる。 find()は左端から、rfind()は右端から検索を行う。

  • find(検索文字列, 開始位置, 終了位置)
  • rfind(検索文字列, 開始位置, 終了位置)
  • startswith(検索文字列, 開始位置, 終了位置)
  • endswith(検索文字列, 開始位置, 終了位置)
[root@BS-PUB-CENT7-01 ~]# cat /tmp/test.txt
aaa 111
bbb 222
ccc 333
ddd 444
eee 555
fff 666
ggg 777
hhh 888
iii 999
jjj 111
abc abc
[root@BS-PUB-CENT7-01 ~]# cat ./test.py
# -*- coding: utf-8 -*
import sys
ld = open(sys.argv[1])
lines = ld.readlines()
ld.close()

for line in lines:
    if line.find('b',1,2) >= 0:
        print line[:-1]
[root@BS-PUB-CENT7-01 ~]# python ./test.py /tmp/test.txt
bbb 222
abc abc

4. 1行目とかXX行目、最後の行といった指定で行を抽出するf

1行目とかXX行目、最後の行といった指定で行の抽出を行う場合は、普通に行を読み込んだ配列を呼び出してやればよい。 なお、その際に改行が含まれている場合はreplaceで改行を除外してやるといいだろう

# -*- coding: utf-8 -*
import sys
ld = open(sys.argv[1])
lines = ld.readlines()
ld.close()

print '1行目(改行あり)'
print lines[0]

print '3行名(改行なし)'
print lines[2].replace('\n','')

print '最終行(改行なし)'
print lines[len(lines)-1].replace('\n','')
[root@BS-PUB-CENT7-01 ~]# cat ./test.py
# -*- coding: utf-8 -*
import sys
ld = open(sys.argv[1])
lines = ld.readlines()
ld.close()

print '1行目(改行あり)'
print lines[0]

print '3行名(改行なし)'
print lines[2].replace('\n','')

print '最終行(改行なし)'
print lines[len(lines)-1].replace('\n','')
[root@BS-PUB-CENT7-01 ~]# cat /tmp/test.txt
aaa 111
bbb 222
ccc 333
ddd 444
eee 555
fff 666
ggg 777
hhh 888
iii 999
jjj 111
abc abc
[root@BS-PUB-CENT7-01 ~]# python ./test.py /tmp/test.txt
1行目(改行あり)
aaa 111

3行名(改行なし)
ccc 333
最終行(改行なし)
abc abc

やはりgrepやawkよりは記述が少し長くなるが、Pythonで同様の処理を行わせる場合はこんな感じで書けばいいようだ。