note.nkmk.me

Jupyter Notebookでシステムコマンドを実行し文字列のリストとして取得

Date: 2018-10-30 / tags: Jupyter Notebook

IPythonでは!lsのように先頭に!をつけることでシステムコマンド(OSコマンド / シェルコマンド)を実行できる。

IPythonをバックエンドで使っているJupyter Notebookでも同様の操作が可能。

Jupyter Notebookのセル上でシステムコマンドを実行できるだけでなく、その標準出力を文字列のリストとしてPythonの変数に格納して使うことができる。

ここでは以下の内容について説明する。

  • 注意点
    • Jupyter NotebookではなくIPythonの仕組み
    • 使えるコマンドは環境に依存
  • !をつけてシステムコマンドを実行
  • 標準出力をPythonのオブジェクト(文字列のリスト)として取得

便宜上、サンプルコードはPythonのコードとして掲載しているが、そのままpythonコマンドで実行しても動かないので注意。

ipynbファイルは以下を参照。

スポンサーリンク

注意点

Jupyter NotebookではなくIPythonの仕組み

冒頭で述べたように、!をつけてシステムコマンドを実行するのはIPythonの仕組み。

内部ではPythonの標準モジュールsubprocessgetoutput()関数を使っている(Python2系ではcommandsモジュール)。

Jupyter Notebookには他のプログラミング言語をkernelとして追加できるが、そのkernelが対応していない限り、!によるシステムコマンドの実行は使えない。

使えるコマンドは環境に依存

あくまでもシステムコマンド(OSコマンド / シェルコマンド)をIPythonの中で実行する仕組みなので、例えば、macOSのターミナルとWindowsのコマンドプロンプトで使えるコマンドが異なる。

ファイルの一覧を表示するコマンドは、macOSを含むUNIX系ではlsだが、Windowsのコマンドプロンプトではdirとなる。

以下のサンプルコードはmacOSのターミナルでJupyter Notebookを起動させた場合の例。

!をつけてシステムコマンドを実行

いくつかのコマンドを実行する例を示す。引数やオプションを指定することも可能。

!date
# 2018年 10月30日 火曜日 22時26分11秒 JST

!echo Hello
# Hello

!ls array*
# array_example.ipynb array_example.py

!ls -l array*
# -rw-r--r--  1 mbp  staff  1995  4 15  2018 array_example.ipynb
# -rw-r--r--  1 mbp  staff   358  4 15  2018 array_example.py

ファイルやディレクトリを処理するコマンドも実行できる。ディレクトリを作成、削除する例。

!mkdir test_dir

!rmdir test_dir

Pythonのコードでファイルやディレクトリを処理することももちろん可能だが、あとでPythonコードとして再利用する必要がなければシステムコマンドで処理してしまうのも便利。

標準出力をPythonのオブジェクト(文字列のリスト)として取得

!で実行したシステムコマンドの標準出力を文字列のリストとして取得し、変数に格納することができる。

output = !date

print(output)
# ['2018年 10月30日 火曜日 22時26分12秒 JST']

取得できるのはIPython.utils.text.SListというIPython独自の型。Pythonの組み込み型listのサブクラスとして定義されている。

print(type(output))
# <class 'IPython.utils.text.SList'>

print(isinstance(output, list))
# True

list型のサブクラスなので、通常のリストのようにlen()で要素数を取得したり、インデックス[n]で要素を取得したりできる。要素は文字列str型。

print(len(output))
# 1

print(output[0])
# 2018年 10月30日 火曜日 22時26分12秒 JST

print(type(output[0]))
# <class 'str'>

標準出力は改行で区切られてリスト化される。

output = !ls -l array*

print(output)
# ['-rw-r--r--  1 mbp  staff  1995  4 15  2018 array_example.ipynb', '-rw-r--r--  1 mbp  staff   358  4 15  2018 array_example.py']

print(len(output))
# 2

print(output[0])
# -rw-r--r--  1 mbp  staff  1995  4 15  2018 array_example.ipynb

print(output[1])
# -rw-r--r--  1 mbp  staff   358  4 15  2018 array_example.py

IPython.utils.text.SList独自の属性nで、改行された文字列を取得できる。

print(output.n)
# -rw-r--r--  1 mbp  staff  1995  4 15  2018 array_example.ipynb
# -rw-r--r--  1 mbp  staff   358  4 15  2018 array_example.py

print(type(output.n))
# <class 'str'>

通常のリストのようにjoin()メソッドを使ってももちろんOK。

print('\n'.join(output))
# -rw-r--r--  1 mbp  staff  1995  4 15  2018 array_example.ipynb
# -rw-r--r--  1 mbp  staff   358  4 15  2018 array_example.py

lsコマンドの注意

改行で区切られてリスト化されると書いたが、lsコマンドは1行で出力されているのに各ファイルが要素としてリスト化される。

!ls array*
# array_example.ipynb array_example.py

output = !ls array*

print(output)
# ['array_example.ipynb', 'array_example.py']

print(len(output))
# 2

これは、lsのデフォルト(オプション無し)の出力が出力先によって異なるため。端末の場合は1行に複数件が出力され、端末ではないパイプやリダイレクトの場合は1行1件で出力される。

Jupyter Notebookのセル上で実行して出力を表示する場合は端末扱いなので1行に複数件が出力されるが、パイプでつなぐと1行1件で出力される。以下の通り。

!ls array*
# array_example.ipynb array_example.py

!ls array* | head
# array_example.ipynb
# array_example.py

出力をPythonのオブジェクトとして取得する場合は出力先が端末ではないので1行1件となり、改行で区切られて行ごとにリスト化される。

明示的に-Cオプションを指定すれば、1行に複数件の出力のまま行ごとにリスト化される。

!ls -C array*
# array_example.ipynb array_example.py

output = !ls -C array*

print(output)
# ['array_example.ipynb\tarray_example.py']

print(len(output))
# 1

print(output[0])
# array_example.ipynb   array_example.py
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事