2章 Unixコマンドの基礎

hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである. 以下の処理を行うプログラムを作成し,hightemp.txtを入力ファイルとして実行せよ. さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.

10. 行数のカウント

行数をカウントせよ.確認にはwcコマンドを用いよ.

In [1]:
import sys
sys.getdefaultencoding()
Out[1]:
'utf-8'
In [2]:
with open('hightemp.txt', 'r', encoding='utf-8') as file:
    print(len(file.readlines()))
24

powershell

Get-Content -Encoding UTF8 .\hightemp.txt | Measure-Object -Line

or

cat -Encoding UTF8 .\hightemp.txt | Measure-Object -Line

でもいける。

11. タブをスペースに置換

タブ1文字につきスペース1文字に置換せよ.確認にはsedコマンド,trコマンド,もしくはexpandコマンドを用いよ.

In [3]:
with open('hightemp.txt', 'r', encoding='utf-8') as file:
    #print(file.readlines())
    replace_space = file.read().replace('\t', ' ')
    #print(list(replace_space))
    print(replace_space)
高知県 江川崎 41 2013-08-12
埼玉県 熊谷 40.9 2007-08-16
岐阜県 多治見 40.9 2007-08-16
山形県 山形 40.8 1933-07-25
山梨県 甲府 40.7 2013-08-10
和歌山県 かつらぎ 40.6 1994-08-08
静岡県 天竜 40.6 1994-08-04
山梨県 勝沼 40.5 2013-08-10
埼玉県 越谷 40.4 2007-08-16
群馬県 館林 40.3 2007-08-16
群馬県 上里見 40.3 1998-07-04
愛知県 愛西 40.3 1994-08-05
千葉県 牛久 40.2 2004-07-20
静岡県 佐久間 40.2 2001-07-24
愛媛県 宇和島 40.2 1927-07-22
山形県 酒田 40.1 1978-08-03
岐阜県 美濃 40 2007-08-16
群馬県 前橋 40 2001-07-24
千葉県 茂原 39.9 2013-08-11
埼玉県 鳩山 39.9 1997-07-05
大阪府 豊中 39.9 1994-08-08
山梨県 大月 39.9 1990-07-19
山形県 鶴岡 39.9 1978-08-03
愛知県 名古屋 39.9 1942-08-02

powershell

powershell の場合、タブ文字は \t` を使う。

$file = Get-Content -Encoding UTF8 .\hightemp.txt
$file -replace "`t", " "

12. 1列目をcol1.txtに,2列目をcol2.txtに保存

各行の1列目だけを抜き出したものをcol1.txtに,2列目だけを抜き出したものをcol2.txtとしてファイルに保存せよ.確認にはcutコマンドを用いよ.

In [4]:
def cut_col_lines(file_name_in:str, cut_idx:int):
    with open(file_name_in,  'r', encoding='utf-8') as file_in:
        col_lines = [line.split()[cut_idx] for line in file_in.readlines()]
        return col_lines
    
def cut_col_out(file_name_in:str, cut_idx:int, file_name_out:str):
    col_lines = cut_col_lines(file_name_in, cut_idx)
    with open(file_name_out, 'w', encoding='utf-8') as file_out:
            for line in col_lines:
                file_out.write(line + '\n')
          
cut_col_out('hightemp.txt', 0, 'col1.txt')
cut_col_out('hightemp.txt', 1, 'col2.txt')

powershell の場合

$file = Get-Content -Encoding UTF8 .\hightemp.txt
$f = $file -replace "`t", " "

$f | %{$_.split(" ")[0]} > col1.txt
$f | %{$_.split(" ")[1]} > col2.txt

13. col1.txtとcol2.txtをマージ

12で作ったcol1.txtとcol2.txtを結合し,元のファイルの1列目と2列目をタブ区切りで並べたテキストファイルを作成せよ.確認にはpasteコマンドを用いよ.

In [5]:
def merge(filename1:str, filename2:str, filename_out:str, separate='\t'):
    with open(filename1, 'r', encoding='utf-8') as file1, open(filename2, 'r', encoding='utf-8') as file2:
        with open(filename_out, 'w', encoding='utf-8') as file_out:
                for l1, l2 in zip(file1.readlines(), file2.readlines()):
                    file_out.write(l1.split()[0] + separate + l2.split()[0] + '\n') 
In [6]:
merge('col1.txt', 'col2.txt', 'merge.txt')

powershell の場合

windows では難しそう

14. 先頭からN行を出力

自然数Nをコマンドライン引数などの手段で受け取り,入力のうち先頭のN行だけを表示せよ.確認にはheadコマンドを用いよ.

In [7]:
import sys
def head(filename, N=1):
    with open(filename, 'r', encoding='utf-8') as file:
        for i in range(N):
            try:
                sys.stdout.write(file.readline())
            except:
                sys.stderr.write('out of range')
In [8]:
head('col1.txt', 26)
高知県
埼玉県
岐阜県
山形県
山梨県
和歌山県
静岡県
山梨県
埼玉県
群馬県
群馬県
愛知県
千葉県
静岡県
愛媛県
山形県
岐阜県
群馬県
千葉県
埼玉県
大阪府
山梨県
山形県
愛知県

powershellの場合

cat -Encoding UTF8 .\col1.txt -Head 25

15. 末尾のN行を出力

自然数Nをコマンドライン引数などの手段で受け取り,入力のうち末尾のN行だけを表示せよ.確認にはtailコマンドを用いよ.

In [9]:
import sys
def tail(filename, N=1):
    with open(filename, 'r', encoding='utf-8') as file:
        sys.stdout.write(''.join(file.readlines()[-N:]))
In [10]:
tail('col1.txt', 25)
高知県
埼玉県
岐阜県
山形県
山梨県
和歌山県
静岡県
山梨県
埼玉県
群馬県
群馬県
愛知県
千葉県
静岡県
愛媛県
山形県
岐阜県
群馬県
千葉県
埼玉県
大阪府
山梨県
山形県
愛知県

powershell の場合

cat -Encoding UTF8 .\col1.txt -Tail 2

16. ファイルをN分割する

自然数Nをコマンドライン引数などの手段で受け取り,入力のファイルを行単位でN分割せよ.同様の処理をsplitコマンドで実現せよ

In [11]:
import sys
import math

def split(filename:str, N=2, filename_out=False):
     with open(filename, 'r', encoding='utf-8') as file:
            lines = file.readlines()
            idx = 0
            length = len(lines)
            ratio = math.ceil(length/N)
            for i in range(N):
                sys.stdout.writelines(lines[idx:idx + ratio])
                print()
                
                if filename_out:
                    with open(filename_out + str(i) + '.txt', 'w', encoding='utf-8') as file_out:
                        file_out.writelines(lines[idx:idx + ratio])
                idx = idx + ratio
In [12]:
split('hightemp.txt', 6)
高知県	江川崎	41	2013-08-12
埼玉県	熊谷	40.9	2007-08-16
岐阜県	多治見	40.9	2007-08-16
山形県	山形	40.8	1933-07-25

山梨県	甲府	40.7	2013-08-10
和歌山県	かつらぎ	40.6	1994-08-08
静岡県	天竜	40.6	1994-08-04
山梨県	勝沼	40.5	2013-08-10

埼玉県	越谷	40.4	2007-08-16
群馬県	館林	40.3	2007-08-16
群馬県	上里見	40.3	1998-07-04
愛知県	愛西	40.3	1994-08-05

千葉県	牛久	40.2	2004-07-20
静岡県	佐久間	40.2	2001-07-24
愛媛県	宇和島	40.2	1927-07-22
山形県	酒田	40.1	1978-08-03

岐阜県	美濃	40	2007-08-16
群馬県	前橋	40	2001-07-24
千葉県	茂原	39.9	2013-08-11
埼玉県	鳩山	39.9	1997-07-05

大阪府	豊中	39.9	1994-08-08
山梨県	大月	39.9	1990-07-19
山形県	鶴岡	39.9	1978-08-03
愛知県	名古屋	39.9	1942-08-02

powershell の場合

powershell の場合難しそう

$split_num      = 2    # 分割

$count = 0;
$file_name = ".\hightemp.txt"
$file = cat -Encoding UTF8 $file_name 
cat -Encoding UTF8 $file_name  -ReadCount ($file.count / $split_num) | 
    ForEach-Object { 
        $count ++
        $cfs = "{0:D3}" -f $count;
        $_ > ($file_name + '_' + $cfs)
    }

17. 1列目の文字列の異なり

1列目の文字列の種類(異なる文字列の集合)を求めよ.確認にはsort, uniqコマンドを用いよ.

In [13]:
def cut_col_set(file_name_in:str, col_idx:int):
    lines = cut_col_lines(file_name_in, col_idx)
    return set(lines)
                
cut_col_set('hightemp.txt', 0)
Out[13]:
{'千葉県',
 '和歌山県',
 '埼玉県',
 '大阪府',
 '山形県',
 '山梨県',
 '岐阜県',
 '愛媛県',
 '愛知県',
 '群馬県',
 '静岡県',
 '高知県'}

powershell の場合

$file = Get-Content -Encoding UTF8 .\hightemp.txt
$f = $file -replace "`t", " "
$f | %{$_.split(" ")[0]} | sort | Get-Unique

18. 各行を3コラム目の数値の降順にソート

各行を3コラム目の数値の逆順で整列せよ(注意: 各行の内容は変更せずに並び替えよ).確認にはsortコマンドを用いよ(この問題はコマンドで実行した時の結果と合わなくてもよい).

In [14]:
import sys
def sort(filename:str, col_idx=2):
    with open(filename, 'r', encoding='utf-8') as file:
        file_lines = [l.replace('\t', ' ') for l in file.readlines()]
        # key値は関数
        sys.stdout.writelines(sorted(file_lines, key=lambda l: l.split()[col_idx], reverse=True))
In [15]:
sort('hightemp.txt', 2)
高知県 江川崎 41 2013-08-12
埼玉県 熊谷 40.9 2007-08-16
岐阜県 多治見 40.9 2007-08-16
山形県 山形 40.8 1933-07-25
山梨県 甲府 40.7 2013-08-10
和歌山県 かつらぎ 40.6 1994-08-08
静岡県 天竜 40.6 1994-08-04
山梨県 勝沼 40.5 2013-08-10
埼玉県 越谷 40.4 2007-08-16
群馬県 館林 40.3 2007-08-16
群馬県 上里見 40.3 1998-07-04
愛知県 愛西 40.3 1994-08-05
千葉県 牛久 40.2 2004-07-20
静岡県 佐久間 40.2 2001-07-24
愛媛県 宇和島 40.2 1927-07-22
山形県 酒田 40.1 1978-08-03
岐阜県 美濃 40 2007-08-16
群馬県 前橋 40 2001-07-24
千葉県 茂原 39.9 2013-08-11
埼玉県 鳩山 39.9 1997-07-05
大阪府 豊中 39.9 1994-08-08
山梨県 大月 39.9 1990-07-19
山形県 鶴岡 39.9 1978-08-03
愛知県 名古屋 39.9 1942-08-02

windows powershell

# CSV を使うと楽

Import-Csv -Encoding UTF8 -Delimiter "`t" -Header "loc1", "loc2", "hight", "date"  .\hightemp.txt  | sort -Property hight -Descending

linuxでは、

sort -r -k 3 hoge.txt

で並び替え

19. 各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる

各行の1列目の文字列の出現頻度を求め,その高い順に並べて表示せよ.確認にはcut, uniq, sortコマンドを用いよ.

In [16]:
import collections

def count_dict(filename_in:str, col_idx=0):
    d = collections.defaultdict(int)
    with open(filename_in, 'r', encoding='utf-8') as file:
        f_lines = file.readlines()
        for line in f_lines:
            d[line.split()[col_idx]] += 1
    return d

def count_sort(filename_in:str, col_idx=0, descending=True):
    d = count_dict(filename_in, col_idx)
    print(sorted(d.items(), key=lambda l:l[1], reverse=descending))
In [17]:
count_dict('hightemp.txt', 0)
count_sort('hightemp.txt', 0)
[('山形県', 3), ('群馬県', 3), ('山梨県', 3), ('埼玉県', 3), ('静岡県', 2), ('岐阜県', 2), ('愛知県', 2), ('千葉県', 2), ('大阪府', 1), ('和歌山県', 1), ('高知県', 1), ('愛媛県', 1)]

powershell の場合

cat -Encoding UTF8 .\hightemp.txt | %{$_.split()[0]} | Group-Object | sort -Property count -Descending

参考リンク