MacOS に free コマンドがないので、ほぼ等価な Python3 での実装
以下のとおり、sysctl と vm_stat 組み合わせて、極力近づけてみました。厳密には shard
ライブラリのメモリローディングとかありそうだけど、常駐しているわけでもないので、、、
chmod a+x free.py
ln -s free.py free
しておけば、そのままコマンドとして振る舞います。もちろん引数はサポートしてません :-)
単発プログラムでライブラリ等の依存もないので、Python 3 実行環境だけあれば、PYTHONPATH
とか気にせずに実行できます。
#!/usr/bin/python3
"""
free.py - free コマンドの等価プログラム
MacOS 用に free コマンドがないので、等価に動作する Python 3 プログラム
"""
# インポート
import sys
from subprocess import run, PIPE, CompletedProcess
from typing import List
#
# free() - free コマンドの等価プログラムの主処理
#
def free() -> int :
"""
free コマンドの主処理
"""
# メモリの使用状況を得る
try :
# 搭載メモリサイズを得るため、sysctl コマンドの出力を得る
result : CompletedProcess = doCmd( [ 'sysctl', 'hw.memsize', ], )
# - 行から搭載メモリサイズを得る - 例 : hw.memsize: 17179869184
line = result.stdout.decode( 'utf-8' ).splitlines()[ 0 ]
m_total = int( line.split()[ 1 ] )
# メモリ使用状況を得るため、vm_stat コマンドの出力を得る
result : CompletedProcess = doCmd( [ 'vm_stat', ], )
# - メモリ情報すべてのキーと値の組を得る
vmstat = {}
# - 先頭行を飛ばして、次の行からキーと値の組を得る - 例 : Pages active: 683617.
# - :. などの末尾にある記号は削除する。また、値はページ (4096 バイト単位) なのでバイトに変換する
for line in result.stdout.decode( 'utf-8' ).splitlines()[ 1 : ] :
words = line.split( ':' )
key = words[ 0 ]
vmstat[ key ] = int( words[ 1 ][ : -1 ] ) * 4096
# - 空き領域を得る (ただし実際使える領域はもっと大きい)
m_free = vmstat[ 'Pages free' ]
# - キャッシュ領域を得る
m_cached = vmstat[ 'Pages purgeable' ] + vmstat[ 'File-backed pages' ]
# - 使用量を計算する - 使用領域からキャッシュを引いたものが実際の使用量
m_used = (
vmstat[ 'Pages wired down' ] + vmstat[ 'Pages active' ] + vmstat[ 'Pages inactive' ]
+ vmstat[ 'Pages speculative' ] + vmstat[ 'Pages occupied by compressor' ]
- m_cached
)
# - 実際のお空き領域は実使用量を引いたもの
m_avail = m_total - m_used
# スワップ使用状況を得るため、sysctl コマンドの出力を得る
result : CompletedProcess = doCmd( [ 'sysctl', 'vm.swapusage', ], )
# - 行からスワップ使用状況を得る - 例 : vm.swapusage: total = 2048.00M used = 1272.50M free = 775.50M
# - 値は MB (1024 * 1024) 単位なのでバイトに変換する
line = result.stdout.decode( 'utf-8' ).splitlines()[ 0 ]
words = line.split()
s_total = int( float( words[ 3 ][ : -1 ] ) * 1024 * 1024 )
s_used = int( float( words[ 6 ][ : -1 ] ) * 1024 * 1024 )
s_free = int( float( words[ 9 ][ : -1 ] ) * 1024 * 1024 )
# 全体の実行結果を free コマンドのフォーマットで出力する
print( ' total used free shared buff/cache available', )
print( '{:5s}{:12d}{:12d}{:12d}{:>12s}{:12d}{:12d}'
.format( "Mem:", m_total, m_used, m_free, '-', m_cached, m_avail, ), )
print( '{:5s}{:12d}{:12d}{:12d}'
.format( "Swap:", s_total, s_used, s_free,), )
# エラーの時の処理 - 例外メッセージ(複数行)を出力する
except Exception as e :
# すべてのエラーメッセージを出力する
for line in str( e ).split( '\n' ) :
print( line, file = sys.stderr )
# エラーコードを返す
return -1
#
# doCmd() - コマンド実行
#
def doCmd( cmd : List[ str ] ) -> CompletedProcess :
"""
コマンドを実行する
:param cmd: コマンド名および引数
:return: 実行結果。リターンコードおよび stdout, stderr の内容を返す
"""
# コマンドを引数とともに実行する
try :
result : CompletedProcess = run( cmd, stdout = PIPE, stderr = PIPE, )
# - コマンドが実行できなかった時はメッセージを設定してエラー
except Exception :
raise Exception( "cannot exec command {}.".format( " ".join( cmd ) ) )
# - 実行結果がエラーなら stderr メッセージを設定してエラー
if result.returncode :
raise Exception( result.stderr.decode( 'utf-8' ) )
return result
#
# エントリポイント - メインを呼び出す
#
if __name__ == '__main__' :
# 主処理を呼び出す
exit( free() )
実行結果
% ./free
total used free shared buff/cache available
Mem: 17179869184 14519988224 262684672 - 2396454912 2659880960
Swap: 3221225472 1577844736 1643380736