作業ノート

様々なまとめ、雑感など

Pythonでコマンドの実行結果の標準出力を取得する

仕事で個人的に使用するコマンドをPythonで書いてみようと思い、Pythonでコマンドを実行してその標準出力を取得する方法を調べた。

$ cat test.py
#!/usr/bin/python

def main():
    for line in exec_cmd('ls -l'):
        print line

def exec_cmd(cmd):
    from subprocess import Popen, PIPE

    p = Popen(cmd.split(' '), stdout=PIPE, stderr=PIPE)
    out, err = p.communicate()

    return [ s for s in out.split('\n') if s ]

if __name__ == '__main__':
    main()

subprocess.Popenを使用する。subprocess.PIPEは標準ストリームに指定するパイプ。

subprocess.Popenでは、コマンドを配列で表現する必要があるので、コマンドラインを文字列で表記して、それをsplit()で分割。

p.communicate()はプロセスと通信し、結果を取得する。返り値は、標準出力、標準エラー出力のタプル(stdout,stderr)

調べている過程でp.wait()を使ってプロセスを実行し、p.stdout.readlines()で取得する方法を先に見つけたが、デッドロックを起こす可能性があるようで、この方法に変更。ただし、この方法でもデータ量が多い場合は使用すべきでない、という注意が公式のドキュメントにあった。

プロセスを実行後、標準出力の結果を行ごとで分割。空行が含まれていたので、リスト内包表記で空行を除外した。

以下が、出力結果。

$ python test.py
total 16
-rw-r--r--  1 te2u  staff  2878  7 15 23:43 2015-07-15_230419.txt
-rw-r--r--  1 te2u  staff   319  7 15 23:48 test.py

参考

CentOS7.1でNetwork ManagerのCUIを使ってネットワーク設定をする

CentOS6から、Network Managerでネットワークを管理するようになっていたようだが、ずっと/etc/sysconfig/network-scriptsを直接編集して設定していた。

今回、CentOS7.1をインストールする機会があったので、Network ManagerのCUIを使って設定してみることにした。

Network ManagerのCUIはnmcli

ホスト名の設定

[root@localhost ~]# nmcli g hostname
localhost.localdomain
[root@localhost ~]# nmcli g hostname centos71.localdomain
[root@localhost ~]# nmcli g hostname
centos71.localdomain
[root@localhost ~]# hostname
centos71.localdomain

gは、generalの略。nmcliでは、最初の引数に指定する値をオブジェクトと呼ぶ。そのオブジェクトに対してコマンドを実行するような体系になっている。

ホスト名の変更は、hostnameコマンドの後に変更後の名称を指定する。指定しなければその時のホスト名が表示される。

変更前の設定内容

今から変更するネットワークの設定内容の確認。

[root@centos71 ~]# nmcli c show
名前    UUID                                  タイプ          デバイス
enp0s3  4b7806c4-fe58-4f47-9e3b-768c7be5f4cc  802-3-ethernet  enp0s3

cconnectionの略。showで、Network Managerで管理しているコネクションの一覧を表示する。

[root@centos71 ~]# nmcli --fields connection c show enp0s3
connection.id:                          enp0s3
connection.uuid:                        4b7806c4-fe58-4f47-9e3b-768c7be5f4cc
connection.interface-name:              enp0s3
connection.type:                        802-3-ethernet
connection.autoconnect:                 no
connection.autoconnect-priority:        0
connection.timestamp:                   1436372715
connection.read-only:                   no
connection.permissions:
connection.zone:                        --
connection.master:                      --
connection.slave-type:                  --
connection.secondaries:
connection.gateway-ping-timeout:        0

一覧にある名前enp0s3がコネクション名。showにこの名前を指定すると、そのコネクションの設定内容が表示される。

--fieldsnmcliのグローバルオプションと呼ばれているもの。特定のフィールドを表示するためのフィルタになる。

ここでは、コネクションenp0s3の基本的な設定を表示している。

[root@centos71 ~]# nmcli --fields ipv4 c show enp0s3
ipv4.method:                            auto
ipv4.dns:
ipv4.dns-search:
ipv4.addresses:
ipv4.gateway:                           --
ipv4.routes:
ipv4.route-metric:                      -1
ipv4.ignore-auto-routes:                no
ipv4.ignore-auto-dns:                   no
ipv4.dhcp-client-id:                    --
ipv4.dhcp-send-hostname:                yes
ipv4.dhcp-hostname:                     --
ipv4.never-default:                     no
ipv4.may-fail:                          yes

--fieldsipv4に変更して、 IPv4に関する設定を表示。DHCPを使用していることがわかる(ipv4.method: auto)。

OS起動時にコネクションを有効化する

[root@centos71 ~]# nmcli c mod enp0s3 connection.autoconnect yes
[root@centos71 ~]# nmcli --fields connection c show enp0s3
connection.id:                          enp0s3
connection.uuid:                        4b7806c4-fe58-4f47-9e3b-768c7be5f4cc
connection.interface-name:              enp0s3
connection.type:                        802-3-ethernet
connection.autoconnect:                 yes
connection.autoconnect-priority:        0
connection.timestamp:                   1436372715
connection.read-only:                   no
connection.permissions:
connection.zone:                        --
connection.master:                      --
connection.slave-type:                  --
connection.secondaries:
connection.gateway-ping-timeout:        0

modmodifyの略。この後にはコネクション名(enp0s3)、フィールド名(connection.autoconnect)、変更値(yes)が続き、それらを指定して実行すると変更される。

OS起動時にコネクションを有効化するには、connection.autoconnectyesにする。

IPを手動で登録する

[root@centos71 ~]# nmcli c mod enp0s3 ipv4.method manual ipv4.addresses 192.168.56.10/24 ipv4.gateway 192.168.56.1
[root@centos71 ~]# nmcli --fields ipv4 c show enp0s3
ipv4.method:                            manual
ipv4.dns:
ipv4.dns-search:
ipv4.addresses:                         192.168.56.10/24
ipv4.gateway:                           192.168.56.1
ipv4.routes:
ipv4.route-metric:                      -1
ipv4.ignore-auto-routes:                no
ipv4.ignore-auto-dns:                   no
ipv4.dhcp-client-id:                    --
ipv4.dhcp-send-hostname:                yes
ipv4.dhcp-hostname:                     --
ipv4.never-default:                     no
ipv4.may-fail:                          yes

IPを手動で登録するには、ipv4.methodmanualにし、ipv4.addressesを所定のIP/prefix(192.168.56.10/24)にする。

ここでは併せて、ゲートウェイも設定した(ipv4.gateway 192.168.56.1)。このように、複数のフィールドを一緒に変更することもできる。

DNSの設定

[root@centos71 ~]# nmcli c mod enp0s3 ipv4.dns "192.168.56.1 8.8.8.8 8.8.4.4" ipv4.dns-search "example.com"
[root@centos71 ~]# nmcli --fields ipv4 c show enp0s3
ipv4.method:                            manual
ipv4.dns:                               192.168.56.1,8.8.8.8,8.8.4.4
ipv4.dns-search:                        example.com
ipv4.addresses:                         192.168.56.10/24
ipv4.gateway:                           192.168.56.1
ipv4.routes:
ipv4.route-metric:                      -1
ipv4.ignore-auto-routes:                no
ipv4.ignore-auto-dns:                   no
ipv4.dhcp-client-id:                    --
ipv4.dhcp-send-hostname:                yes
ipv4.dhcp-hostname:                     --
ipv4.never-default:                     no
ipv4.may-fail:                          yes

DNSサーバはipv4.dnsで設定する。IPをスペースで区切り、全体をダブルクォートで囲むことで、DNSサーバを複数設定することができる。

検索ドメインはipv4.dns-searchを設定する。

設定の確認

[root@centos71 ~]# ip a show enp0s3
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 08:00:27:d5:4c:8e brd ff:ff:ff:ff:ff:ff
    inet 192.168.56.10/24 brd 192.168.56.255 scope global enp0s3
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fed5:4c8e/64 scope link
       valid_lft forever preferred_lft forever

ipで設定内容を確認してみたところ。inetが所定のIPになっている。

コマンドで指定したenp0s3は、コネクション名ではなくデバイス名。上記のフィールド名connection.interface-nameの値と同じ。

参考

bashのヒアドキュメントで、root権限下のディレクトリにスクリプトを作成する

以前、bashのヒアドキュメントを使ったスクリプトの作成で、catを使ったスクリプトの作成方法を紹介した。

[vagrant@localhost ~]$ cat <<'EOT' > test.sh
> #!/bin/bash
> set -eu
>
> echo 'test'
>
> EOT

[vagrant@localhost ~]$ ls -l test.sh
-rw-rw-r-- 1 vagrant vagrant 34  7月  1 22:30 2015 test.sh

作成先が実行ユーザで書き込みできるディレクトリなら問題ないが、root権限が必要なディレクトリでは

[vagrant@localhost ~]$ cat <<'EOT' > /usr/local/bin/test.sh
> #!/bin/bash
> set -eu
>
> echo 'test'
>
> EOT
-bash: /usr/local/bin/test.sh: 許可がありません

作成できない。

作成するには、catの標準出力をパイプでつなぎ、sudoteeを実行して、ファイル名を出力先にする。

[vagrant@localhost ~]$ cat <<'EOT' | sudo tee /usr/local/bin/test.sh > /dev/null
> #!/bin/bash
> set -eu
>
> echo 'test'
>
> EOT
[vagrant@localhost ~]$ ls -l /usr/local/bin
合計 4
-rw-r--r-- 1 root root 34  7月  1 22:40 2015 test.sh

追記するには、tee-aオプションを指定する。

[vagrant@localhost ~]$ cat <<'EOT' | sudo tee -a /usr/local/bin/test.sh > /dev/null
> echo 'test2'
>
> EOT

[vagrant@localhost ~]$ cat /usr/local/bin/test.sh
#!/bin/bash
set -eu

echo 'test'

echo 'test2'

teeは、標準入力の内容を指定したファイルと標準出力に出力する。

上記の方法では、teeを使って入力内容をファイルに出力しているが、標準出力は必要ないため/dev/nullにリダイレクトしている。

参考