学習の備忘録として記録していきます。
初学者のため、情報に誤りがある場合、ご指摘いただけると幸いです。
前置き
- 42tokyoでC言語を学習中。(9月入学)
- Pythonは入学前に軽く触れた程度
Bundleで背伸びして、サイバーセキュリティに関する洋書のセットを買ったものの(とても安い。まだセールやっているのでリンク貼っておきます)、ハードディスクの肥やしになりそうな気配だったので、記事に残して、モチベを保ちつつ進めていけたらなあと考えて始めました。
42でやっているのは今のところC言語オンリー。オブジェクト指向だのなんなのは、まだまだ触らせてもらえません。そのため、まずは文法を独習Pythonで勉強しました。C言語をやっているだけあって、基本的なPythonプログラミングの概念はすんなり入っていく気がします。42では標準ライブラリやフレームワークなどをフルスクラッチで再実装するといういわゆる車輪の再発明をすることが多く、やっている最中は意味があるのかと思ってしまいますが、自分で低レベルから実装を行うとブラックボックス化してる中身の解像度が上がって、知識が体系化されやすいのかなと思いました。
セキュリティに関しては、完全にまったくの初心者のため、まずは全体を俯瞰してみるために、"Cybersecurity Career Master Plan"という本で、フレームワークをつかみました。(全部は読み切れていないですが...)
この本はセキュリティという分野に関する、ロードマップ的なお話でしたので、次は実際に手を動かして学ぶ本を選びました。"Python for Security and Networking"という本ですね。本文ではこの本を実際に動かしつつ、まとめていきたいと思います。
System Programming with python
私たちが普段利用している便利なアプリケーションの内部では、OSがハードウェアとソフトウェアの仲立ちをしてくれています。ユーザとしてはその働きを意識せずとも、恩恵を受けられるよう設計されていますが、OSを意識すると、魔法のようにみえるコンピュータの仕組みが少し見えてきます。
PythonではOS操作のためにOS moduleが標準ライブラリとして搭載されています。
OSModuleの主要な機能:
-
ファイルやディレクトリの操作
- ファイルの作成、削除、移動、名前変更など
-
プロセス管理
- プロセスの生成、終了、スリープなど
-
環境変数の取得や設定
-
os.environ
を使用して環境変数にアクセスしたり、設定したり
-
-
ユーザーやグループの情報の取得
-
os.getuid()
やos.getgid()
を使用してユーザーIDやグループIDを取得
-
os.walk()を使って再帰的にファイルを探索
import os
#os.getcwd()で現在のパスを取得できる
directory = os.getcwd()
extensions = ['.jpeg', '.txt', '.exe']
for extension in extensions:
for path, folder, files in os.walk("."):
for file in files:
if file.endswith(extension):
print(os.path.join(path, file))
./test.jpeg
./test.txt
./test.exe
os.walk()は3つの要素から成るタプルを返します。
1.(root): 再帰的に走査されるディレクトリのパスです。このディレクトリから始まり、そのサブディレクトリも走査されます。
2.(directories): root で指定されたディレクトリ内のサブディレクトリのリストです。os.walk() は再帰的に走査するため、各サブディレクトリの中身も走査されます。
3.(files): root で指定されたディレクトリ内のファイルのリストです。このリストには、そのディレクトリ内のすべてのファイルが含まれます
pythonスクリプトでZIPファイルを読み込む
import zipfile
def list_files_in_zip(filename):
with zipfile.ZipFile(filename) as myzip:
for zipinfo in myzip.infolist():
yield zipinfo.filename
for filename in list_files_in_zip("test.zip"):
print(filename)
file1.txt
file2.txt
zipファイルの読み書きはzipfileを使います。
yieldは処理をその時点で中断し、次の呼び出し時にそこから再開します。returnの場合、その時点で関数は終了し、次の呼び出しは関数の頭から始まります。
subprocess module
OSコマンドと通信したり、プログラムを実行し、アウトプットを受け取る方法として、subprocess moduleが使われます。もっとも単純な例としてrun()メソッドの使用が挙げられます。
import subprocess
# 外部コマンドを実行して標準出力を取得する例
result = subprocess.run(['ls', '-l'], capture_output=True)
# 実行結果の標準出力を表示
print("STDOUT:")
print(result.stdout.decode("utf-8"))
# 実行結果の標準エラー出力を表示
print("STDERR:")
print(result.stderr.decode("utf-8"))
# 実行結果の戻り値(終了ステータス)を表示
print("Return code:", result.returncode)
STDOUT:
total 20
-rw-r--r-- 1 ****** ****** 304 Feb 22 07:54 example.zip
-rw-r--r-- 1 ****** ****** 22 Feb 22 07:54 example.zip.temp
drwxr-xr-x 2 ****** ****** 4096 Feb 22 07:52 extracted_files
-rw-r--r-- 1 ****** ****** 0 Feb 22 07:52 file1.txt
-rw-r--r-- 1 ****** ****** 0 Feb 22 07:52 file2.txt
-rw-r--r-- 1 ****** ****** 0 Feb 22 07:52 file3.txt
-rw-r--r-- 1 ****** ****** 426 Feb 22 21:35 osmani.py
-rw-r--r-- 1 ****** ****** 0 Feb 22 07:26 test.exe
-rw-r--r-- 1 ****** ****** 0 Feb 22 07:26 test.jpeg
-rw-r--r-- 1 ****** ****** 0 Feb 22 07:26 test.txt
-rw-r--r-- 1 ****** ****** 314 Feb 22 07:56 test.zip
STDERR:
Return code: 0
runメソッドは完了したプロセスのインスタンスを返します。このインスタンスは終了ステータス、標準出力(stdout)、標準エラー出力(stderr)の属性を持ちます。
デフォルトでは通信はバイトで行われます。そのためインプット、アウトプットはバイトデータになります。もし通信がテキストモードで行われている場合、インプット、アウトプットはテキストデータとなります。
外部プロセスを実行する方法として、runメソッドの他にpopenメソッドがあります。
runメソッドが外部プロセスの終了を待機するのに対し、popenメソッドは外部プロセスの終了を待たず、プロセスが非同期的に実行されます。(communicateメソッドを用いることで任意のタイミングでデータを渡したり受け取ったりできます)
import subprocess
# pingコマンドの実行
host = "www.google.com"
process = subprocess.Popen(["ping", "-c", "5", host], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
# 実行結果の取得
stdout, stderr = process.communicate()
# 実行結果の表示
if process.returncode == 0:
print("Ping successful:")
print(stdout)
else:
print("Ping failed:")
print(stderr)
Ping successful:
PING www.google.com (142.251.42.132) 56(84) bytes of data.
64 bytes from nrt12s45-in-f4.1e100.net (142.251.42.132): icmp_seq=1 ttl=116 time=5.71 ms
64 bytes from nrt12s45-in-f4.1e100.net (142.251.42.132): icmp_seq=2 ttl=116 time=5.13 ms
64 bytes from nrt12s45-in-f4.1e100.net (142.251.42.132): icmp_seq=3 ttl=116 time=4.62 ms
64 bytes from nrt12s45-in-f4.1e100.net (142.251.42.132): icmp_seq=4 ttl=116 time=4.88 ms
64 bytes from nrt12s45-in-f4.1e100.net (142.251.42.132): icmp_seq=5 ttl=116 time=4.23 ms
--- www.google.com ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4497ms
rtt min/avg/max/mdev = 4.231/4.913/5.711/0.497 ms
pingコマンド…CMP(Internet Control Message Protocol)エコーリクエストを送信し、ネットワークの可用性と応答時間を検証するためのコマンドラインツール
subprocessを使って仮想環境を設定する
pythonを使うことでプロセスを自動化することができます。以下は仮想環境を設定した後にrequirements.txtを探し、pipを用いてパッケージをインストールする作業を自動化するスクリプトです
import subprocess
from pathlib import Path
import sys
VENV_NAME = '.venv'
REQUIREMENTS = 'requirements.txt'
process = subprocess.run(['which', 'python3'], capture_output=True, text=True)
print("OS:" ,sys.platform)
print(process.returncode)
if process.returncode != 0:
raise OSError('python is not installed')
python_process = process.stdout.strip()
print(f'python is found in: {python_process}')
process = subprocess.run('echo "$SHELL"', shell=True, capture_output=True,text=True)
shell_bin = process.stdout.split('/')[-1]
create_env = subprocess.run([python_process, '-m', 'venv', VENV_NAME], check = True)
if create_env.returncode == 0:
print(f'your venv {VENV_NAME} has been created')
else:
print(f'your venv {VENV_NAME} has not been created')
pip_process = f'{VENV_NAME}/bin/pip3'
if Path(REQUIREMENTS).exists():
print(f'Requirements file is found. Installing requirements...')
subprocess.run([pip_process, 'install', '-r', REQUIREMENTS])
print('process completed')
OS: linux
0
python is found in: /usr/bin/python3
your venv .venv has been created
Requirements file is found. Installing requirements...
process completed
スレッドをpythonで管理する
threadingとは?
同一のメモリ空間を同時に利用し、プログラムを並列に実行するプログラミングのテクニックです。マルチタスクのようなものを実現できます。スレッドはプロセス内で動作し、プロセスと同じメモリ空間を共有しますが、個々のスレッドは独立して実行されます。
スレッドは以下のようなサイクルを持ちます。
- New(スレッドは開始していないが、メモリは割り当てられている)
- Runnable(実行可能な状態で待機中)
- Running(スレッドがCPUを占有して実行中)
- Not-running(入出力等の割り込みによって停止中)
- Finished(実行終了)
pythonではスレッドを実装するためにthredingモジュールが用意されています。
import threading
def mytask():
print(f"hello world {threading.current_thread()}")
myFirstThread = threading.Thread(target=mytask)
myFirstThread.start()
hello world <Thread(Thread-1 (mytask), started 139862573180608)>
利点
- スレッドを使うと、同じメモリ空間をメインと共有できるので、アクセスややり取りが容易
- メモリのリソースを節約できる
- 同時にプロセスを実行するようなアプリケーションの設計がシンプルになる