[01] subprocessの代替パッケージ「sh」を使ってみる


|本シリーズのトップページ|
|:--|
|https://qiita.com/robozushi10/items/fabde36bb9a312347bc7|

# はじめに

一般的に Python で Linux コマンドを使う場合は `subprocess` を使うこと多い.
が、標準パッケージでは無いものの subprocess の代替である「`sh`」(`amoffat/sh`) も便利である.[^1]

個人的な考えではあるが、「sh[^2]」は標準パッケージでは無いので、職場などで使い込むと
他者の迷惑になり兼ねない。しかし、適度に使う分には `subprocess` よりも速く実装できて
好都合なので書き残しておく.


本項では「**手抜きをせずに『sh』を使う場合**」を記す.
そして、<font color="red">本項の実装だと subprocess に置き換えるメリットは少ない</font> かと思う.
逆に、<font color="blue"> [次項](https://qiita.com/robozushi10/items/7fee38be93b4c0fc014c) の実装だとメリットが結構ある</font> と思う.


# セットアップ

pip 等で `sh` をインストールする.
詳細は本項末尾の「[参考情報](#参考情報)」に記した.


# 使用例

下記シェルスクリプト「001.sh」相当を、パッケージ「sh」を使って Python で実装する


## 001.sh

### 処理概要

001.sh の処理内容は、次の 1〜5 の処理である.
しかし、個人的には Python の subprocess でこの処理を実装しようとすると相当辛い.

```
1. ディレクトリ /tmp/<一意な文字列>/ を作成する
2. ディレクトリ /tmp/<一意な文字列>/ に移動する
3. ファイル /tmp/<一意な文字列>/log を作成する
4. pwd と date の実行結果を /tmp/<一意な文字列>/log に追加書き込みする
5. ファイル /tmp/<一意な文字列>/log を表示する
```

### コード

```bash
#!/bin/bash
# 一意な値を生成して UUID に格納する
UUID=`uuidgen`
mkdir -p /tmp/$UUID
cd /tmp/$UUID
touch log
pwd  >> log
date +"%Y/%m/%d %H:%M:%S.%6N" >> log
echo "/tmp/$UUID/log に書き込みました"
cat -n /tmp/$UUID/log
```

### 実行結果

```
/tmp/a0db1349-a758-44c9-b017-2b90f184ba84/log に書き込みました
     1	/tmp/a0db1349-a758-44c9-b017-2b90f184ba84
     2	2021/08/04 20:44:44.374856
```

　

## 001.py

上述のシェルスクリプトを、Python パッケージ「sh」を使って "**真面目に**" 置き換えた版である.

この実装方法だと「sh」の使用方法を調べつつになるので、
subprocess からの置き換えるメリットはほとんどないと思う.


### コード

冗長かも知れないが、シェルでの実行した場合をコメントとして記している.

```python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sh

if __name__ == '__main__':

  # 「buf=03310e2f-f61f-494c-9eb8-dfe4d26660f4」となる
  buf = sh.uuidgen().rstrip()

  # 「echo $buf | sed "s/$buf/-/g"」により「UUID=03310e2ff61f494c9eb8dfe4d26660f4」を得る
  UUID = sh.sed(sh.echo(buf), 's/-//g').rstrip()

  # TMPDIR=/tmp/03310e2ff61f494c9eb8dfe4d26660f4
  TMPDIR = f'/tmp/{UUID}'

  # mkdir -p /tmp/03310e2ff61f494c9eb8dfe4d26660f4
  sh.mkdir('-p', TMPDIR)

  # cd /tmp/03310e2ff61f494c9eb8dfe4d26660f4
  sh.cd(f'/tmp/{UUID}')

  # 追記モードでファイル「/tmp/03310e2ff61f494c9eb8dfe4d26660f4/log」を作成する
  FN = open(f'/tmp/{UUID}/log', 'a')
  
  # pwd >> /tmp/03310e2ff61f494c9eb8dfe4d26660f4/log
  sh.pwd(_out=FN)

  # date "+%Y/%m/%d %H:%M:%S.%6N" >> /tmp/03310e2ff61f494c9eb8dfe4d26660f4/log
  sh.date('+%Y/%m/%d %H:%M:%S.%6N', _out=FN).rstrip()
  # echo '/tmp/03310e2ff61f494c9eb8dfe4d26660f4/log に書き込みました'
  print(sh.echo(f'{FN.name} に書き込みました').rstrip())
  
  # cat -n /tmp/03310e2ff61f494c9eb8dfe4d26660f4/log
  for l in sh.cat('-n', f'/tmp/{UUID}/log'):
    print(l.rstrip())

```

### 実行結果

```bash
/tmp/f7d811e7d8b042baa0dac5fc38db3a1b/log に書き込みました
     1	/tmp/f7d811e7d8b042baa0dac5fc38db3a1b
     2	2021/08/04 20:21:55.891392 
```


# 参考情報

|項目|URL|一言|
|:--|:--|:--|
|Pypi sh|https://pypi.org/project/sh/||
|公式ドキュメント - トップページ|https://amoffat.github.io/sh/||
|公式ドキュメント - API逆引き|https://amoffat.github.io/sh/reference.html||
|チュートリアル|https://amoffat.github.io/sh/tutorials|ここで実現したい機能を検索しながら使うことが多い|
|もた日記|https://wonderwall.hatenablog.com/entry/2017/07/30/083000||


# 検討環境

```bash
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.4 LTS"
```

　

以上

[^1]: 私は職場で別の方が使用していて知った
[^2]: パッケージ名が「sh」だと検索し辛い..