シェルコマンドを実行し、その出力 を文字列として返す関数 を作成したいのですが、エラーまたは成功のメッセージです。コマンドラインで得られたのと同じ結果を得たいだけです。
そのようなことをするコード例は何でしょうか。
例えば:
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'
これはもっと簡単ですが、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
そんな感じ:
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,
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)
これは トリッキー but スーパーシンプル の解決策であり、多くの状況で機能します。
import os
os.system('sample_cmd > tmp')
print open('tmp', 'r').read()
コマンドの出力で一時ファイル(ここではtmp)が作成され、そこから必要な出力を読み取ることができます。
コメントからの特別な注意:あなたは一度限りの仕事の場合にはtmpファイルを削除することができます。これを数回行う必要がある場合は、tmpを削除する必要はありません。
os.remove('tmp')
Python 3.5の場合:
import subprocess
output = subprocess.run("ls -l", Shell=True, stdout=subprocess.PIPE,
universal_newlines=True)
print(output.stdout)
私は同じ問題を抱えていたしかし、これを行うこれの非常に簡単な方法を考え出した
import subprocess
output = subprocess.getoutput("ls -l")
print(output)
お役に立てば幸いです
注:subprocess.getoutput()
はpython2では機能しないため、このソリューションはpython3固有のものです。
以下のコマンドを使用して任意のシェルコマンドを実行できます。私はそれらをUbuntuで使っています。
import os
os.popen('your command here').read()
最新のPythonソリューション(> = 3.1):
res = subprocess.check_output(lcmd, stderr=subprocess.STDOUT)
マイレージが変わるかもしれませんが、@ 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')
以下の条件で同じ問題の風味が少し異なります。
これまでの答えを組み合わせて微調整して、次のことを思いついた。
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)
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...'
もしあなたが複数のファイルに対してシェルコマンドを実行する必要があるなら、これは私にとってはトリックでした。
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の解決策を見ただけです。先に行き、それを取り入れました。
@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でコマンドを実行したときとまったく同じように動作します。
出力をテキストファイルにリダイレクトしてから読み戻すことができます。
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))
たとえば、execute( 'ls -ahl')は3つまたは4つの戻り値とOSプラットフォームを区別しました。
以下の機能
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 ""
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