web-dev-qa-db-ja.com

Shellコマンドを実行して出力を取得する

シェルコマンドを実行し、その出力 を文字列として返す関数 を作成したいのですが、エラーまたは成功のメッセージです。コマンドラインで得られたのと同じ結果を得たいだけです。

そのようなことをするコード例は何でしょうか。

例えば:

def run_command(cmd):
    # ??????

print run_command('mysqladmin create test -uroot -pmysqladmin12')
# Should output something like:
# mysqladmin: CREATE DATABASE failed; error: 'Can't create database 'test'; database exists'
718
Silver Light

これはもっと簡単ですが、Unix(Cygwinを含む)でしか動作しません。

import commands
print commands.getstatusoutput('wc -l file')

(return_value、output)でTupleを返します。

これはpython2.7でのみ機能します。python3では使用できません。両方で機能する解決策としては、代わりにsubprocessモジュールを使用してください。

import subprocess
output=subprocess.Popen(["date"],stdout=PIPE)
response=output.communicate()
print response
178
byte_array

そんな感じ:

def runProcess(exe):    
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    while(True):
        # returns None while subprocess is running
        retcode = p.poll() 
        line = p.stdout.readline()
        yield line
        if retcode is not None:
            break

Stderrをstdoutにリダイレクトしていることに注意してください。それはあなたが望むものとは限りませんが、エラーメッセージも欲しいのです。

この関数は 行ごとに1行ずつ表示します (通常、出力全体を取得するにはサブプロセスが終了するのを待つ必要があります)。

あなたの場合は、使い方は次のようになります。

for line in runProcess('mysqladmin create test -uroot -pmysqladmin12'.split()):
    print line,
104
vartec

Vartecの answerはすべての行を読むわけではないので、私は次のようなバージョンを作りました。

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT)
    return iter(p.stdout.readline, b'')

使い方は一般に認められている答えと同じです。

command = 'mysqladmin create test -uroot -pmysqladmin12'.split()
for line in run_command(command):
    print(line)
60
Max Ekman

これは トリッキー but スーパーシンプル の解決策であり、多くの状況で機能します。

import os
os.system('sample_cmd > tmp')
print open('tmp', 'r').read()

コマンドの出力で一時ファイル(ここではtmp)が作成され、そこから必要な出力を読み取ることができます。

コメントからの特別な注意:あなたは一度限りの仕事の場合にはtmpファイルを削除することができます。これを数回行う必要がある場合は、tmpを削除する必要はありません。

os.remove('tmp')
41
Mehdi1902

Python 3.5の場合:

import subprocess

output = subprocess.run("ls -l", Shell=True, stdout=subprocess.PIPE, 
                        universal_newlines=True)
print(output.stdout)
22
cmlaverdiere

私は同じ問題を抱えていたしかし、これを行うこれの非常に簡単な方法を考え出した

import subprocess
output = subprocess.getoutput("ls -l")
print(output)

お役に立てば幸いです

注:subprocess.getoutput()はpython2では機能しないため、このソリューションはpython3固有のものです。

18
itz-azhar

以下のコマンドを使用して任意のシェルコマンドを実行できます。私はそれらをUbuntuで使っています。

import os
os.popen('your command here').read()
15
Muhammad Hassan

最新のPythonソリューション(> = 3.1):

 res = subprocess.check_output(lcmd, stderr=subprocess.STDOUT)
13
zlr

マイレージが変わるかもしれませんが、@ senderleがPython 2.6.5上のWindowsでVartecのソリューションを試してみることを試みましたが、エラーが発生し、他のソリューションは機能しませんでした。私のエラーはWindowsError: [Error 6] The handle is invalidです。

期待した出力を返すためには、PIPEをすべてのハンドルに割り当てなければならないことがわかりました。

import subprocess

def run_command(cmd):
    """given Shell command, returns communication Tuple of stdout and stderr"""
    return subprocess.Popen(cmd, 
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE).communicate()

[0]はTupleの最初の要素stdoutを取得します):

run_command('tracert 11.1.0.1')[0]

私はさまざまなハンドルを使用するカスタムシステムで作業しているので、より多くを学んだ後、私はこれらのパイプ引数が必要だと思うので、私はすべての標準入力を直接制御する必要がありました。

(Windowsで)コンソールポップアップを停止するには、次の操作を行います。

def run_command(cmd):
    """given Shell command, returns communication Tuple of stdout and stderr"""
    # instantiate a startupinfo obj:
    startupinfo = subprocess.STARTUPINFO()
    # set the use show window flag, might make conditional on being in Windows:
    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
    # pass as the startupinfo keyword argument:
    return subprocess.Popen(cmd,
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE, 
                            startupinfo=startupinfo).communicate()

run_command('tracert 11.1.0.1')
10
Aaron Hall

以下の条件で同じ問題の風味が少し異なります。

  1. STDOUTメッセージがSTDOUTバッファに蓄積されるにつれて(すなわちリアルタイムで)、STDOUTメッセージをキャプチャして返す。
    • @vartecは彼のジェネレータと 'yield'を使ってこのPythonで解決しました
      上記のキーワード
  2. すべてのSTDOUT行を表示します( STDOUTバッファを完全に読み取ることができる前にプロセスが終了した場合でも
  3. 高頻度でプロセスをポーリングするCPUサイクルを無駄にしない
  4. サブプロセスの終了コードを確認してください。
  5. ゼロ以外のエラー戻りコードを受け取った場合は、STDERR(STDOUTとは別に)を印刷してください。

これまでの答えを組み合わせて微調整して、次のことを思いついた。

import subprocess
from time import sleep

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE,
                         Shell=True)
    # Read stdout from subprocess until the buffer is empty !
    for line in iter(p.stdout.readline, b''):
        if line: # Don't print blank lines
            yield line
    # This ensures the process has completed, AND sets the 'returncode' attr
    while p.poll() is None:                                                                                                                                        
        sleep(.1) #Don't waste CPU-cycles
    # Empty STDERR buffer
    err = p.stderr.read()
    if p.returncode != 0:
       # The run_command() function is responsible for logging STDERR 
       print("Error: " + str(err))

このコードは前の答えと同じように実行されるでしょう:

for line in run_command(cmd):
    print(line)
8
The Aelfinn

subprocessの最初のコマンドを分割するのは、面倒で面倒かもしれません。

あなた自身を助けるためにshlex.splitを使ってください。

サンプルコマンド

git log -n 5 --since "5 years ago" --until "2 year ago"

コード

from subprocess import check_output
from shlex import split

res = check_output(split('git log -n 5 --since "5 years ago" --until "2 year ago"'))
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'
2

もしあなたが複数のファイルに対してシェルコマンドを実行する必要があるなら、これは私にとってはトリックでした。

import os
import subprocess

# Define a function for running commands and capturing stdout line by line
# (Modified from Vartec's solution because it wasn't printing all lines)
def runProcess(exe):    
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    return iter(p.stdout.readline, b'')

# Get all filenames in working directory
for filename in os.listdir('./'):
    # This command will be run on each file
    cmd = 'nm ' + filename

    # Run the command and capture the output line by line.
    for line in runProcess(cmd.split()):
        # Eliminate leading and trailing whitespace
        line.strip()
        # Split the output 
        output = line.split()

        # Filter the output and print relevant lines
        if len(output) > 2:
            if ((output[2] == 'set_program_name')):
                print filename
                print line

編集:J.F. Sebastianの提案でMax Perssonの解決策を見ただけです。先に行き、それを取り入れました。

2
Ethan Strider

@senderleによると、あなたが私のようにpython3.6を使っているのなら:

def sh(cmd, input=""):
    rst = subprocess.run(cmd, Shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=input.encode("utf-8"))
    assert rst.returncode == 0, rst.stderr.decode("utf-8")
    return rst.stdout.decode("utf-8")
sh("ls -a")

Bashでコマンドを実行したときとまったく同じように動作します。

0
Neo li

出力をテキストファイルにリダイレクトしてから読み戻すことができます。

import subprocess
import os
import tempfile

def execute_to_file(command):
    """
    This function execute the command
    and pass its output to a tempfile then read it back
    It is usefull for process that deploy child process
    """
    temp_file = tempfile.NamedTemporaryFile(delete=False)
    temp_file.close()
    path = temp_file.name
    command = command + " > " + path
    proc = subprocess.run(command, Shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    if proc.stderr:
        # if command failed return
        os.unlink(path)
        return
    with open(path, 'r') as f:
        data = f.read()
    os.unlink(path)
    return data

if __== "__main__":
    path = "Somepath"
    command = 'ecls.exe /files ' + path
    print(execute(command))
0
M. R.

たとえば、execute( 'ls -ahl')は3つまたは4つの戻り値とOSプラットフォームを区別しました。

  1. 出力はありませんが、正常に実行されました。
  2. 空行を出力し、正常に実行された
  3. 実行失敗
  4. 何かを出力し、正常に実行する

以下の機能

def execute(cmd, output=True, DEBUG_MODE=False):
"""Executes a bash command.
(cmd, output=True)
output: whether print Shell output to screen, only affects screen display, does not affect returned values
return: ...regardless of output=True/False...
        returns Shell output as a list with each elment is a line of string (whitespace stripped both sides) from output
        could be 
        [], ie, len()=0 --> no output;    
        [''] --> output empty line;     
        None --> error occured, see below

        if error ocurs, returns None (ie, is None), print out the error message to screen
"""
if not DEBUG_MODE:
    print "Command: " + cmd

    # https://stackoverflow.com/a/40139101/2292993
    def _execute_cmd(cmd):
        if os.name == 'nt' or platform.system() == 'Windows':
            # set stdin, out, err all to PIPE to get results (other than None) after run the Popen() instance
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, Shell=True)
        else:
            # Use bash; the default is sh
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, Shell=True, executable="/bin/bash")

        # the Popen() instance starts running once instantiated (??)
        # additionally, communicate(), or poll() and wait process to terminate
        # communicate() accepts optional input as stdin to the pipe (requires setting stdin=subprocess.PIPE above), return out, err as Tuple
        # if communicate(), the results are buffered in memory

        # Read stdout from subprocess until the buffer is empty !
        # if error occurs, the stdout is '', which means the below loop is essentially skipped
        # A prefix of 'b' or 'B' is ignored in Python 2; 
        # it indicates that the literal should become a bytes literal in Python 3 
        # (e.g. when code is automatically converted with 2to3).
        # return iter(p.stdout.readline, b'')
        for line in iter(p.stdout.readline, b''):
            # # Windows has \r\n, Unix has \n, Old mac has \r
            # if line not in ['','\n','\r','\r\n']: # Don't print blank lines
                yield line
        while p.poll() is None:                                                                                                                                        
            sleep(.1) #Don't waste CPU-cycles
        # Empty STDERR buffer
        err = p.stderr.read()
        if p.returncode != 0:
            # responsible for logging STDERR 
            print("Error: " + str(err))
            yield None

    out = []
    for line in _execute_cmd(cmd):
        # error did not occur earlier
        if line is not None:
            # trailing comma to avoid a newline (by print itself) being printed
            if output: print line,
            out.append(line.strip())
        else:
            # error occured earlier
            out = None
    return out
else:
    print "Simulation! The command is " + cmd
    print ""
0
Jerry T

subprocess pythonモジュールを使用すると、STDOUT、STDERR、およびコマンドの戻りコードを別々に処理できます。完全なコマンド呼び出し側の実装の例を見ることができます。もちろん、必要に応じてtry..exceptで拡張することもできます。

下記の関数はSTDOUT、STDERR、およびReturnコードを返すので、他のスクリプトでそれらを処理できます。

import subprocess

def command_caller(command=None)
    sp = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, Shell=False)
    out, err = sp.communicate()
    if sp.returncode:
        print(
            "Return code: %(ret_code)s Error message: %(err_msg)s"
            % {"ret_code": sp.returncode, "err_msg": err}
            )
    return sp.returncode, out, err
0
milanbalazs