program tip

셸 명령 실행 및 출력 캡처

radiobox 2020. 9. 29. 07:34
반응형

셸 명령 실행 및 출력 캡처


셸 명령을 실행하고 출력 을 문자열로 반환하는 함수를 작성하고 싶습니다 . 명령 줄에서 얻은 것과 동일한 결과를 얻고 싶습니다.

그런 일을하는 코드 예제는 무엇일까요?

예를 들면 :

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'

이 질문에 대한 답은 사용중인 Python 버전에 따라 다릅니다. 가장 간단한 방법은 subprocess.check_output함수 를 사용하는 것입니다.

>>> subprocess.check_output(['ls', '-l'])
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

check_output인수 만 입력으로받는 단일 프로그램을 실행합니다. 1에 인쇄 된대로 정확하게 결과를 반환합니다 stdout. 에 입력을 작성해야하는 경우 또는 섹션으로 stdin건너 뜁니다 . 복잡한 쉘 명령을 실행하려면 이 답변 끝에 있는 참고를 참조하십시오 .runPopenshell=True

check_output함수는 여전히 광범위하게 사용되는 거의 모든 버전의 Python에서 작동합니다 (2.7+). 2 그러나 최신 버전의 경우 더 이상 권장되는 접근 방식이 아닙니다.

최신 버전의 Python (3.5 이상) : run

Python 3.5 이상을 사용 중이고 이전 버전과의 호환성이 필요하지 않은 경우 run함수 를 사용하는 것이 좋습니다. subprocess모듈에 대해 매우 일반적이고 높은 수준의 API를 제공합니다 . 프로그램의 출력을 캡처하려면 subprocess.PIPE플래그를 stdout키워드 인수에 전달하십시오. 그런 다음 stdout반환 된 CompletedProcess객체 속성에 액세스 합니다.

>>> import subprocess
>>> result = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE)
>>> result.stdout
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

반환 값은 bytes객체이므로 적절한 문자열을 원한다면 필요 decode합니다. 호출 된 프로세스가 UTF-8로 인코딩 된 문자열을 반환한다고 가정합니다.

>>> result.stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

이것은 모두 한 줄로 압축 할 수 있습니다.

>>> subprocess.run(['ls', '-l'], stdout=subprocess.PIPE).stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

프로세스의에 입력을 stdin전달 bytes하려면 input키워드 인수에 객체를 전달합니다 .

>>> cmd = ['awk', 'length($0) > 5']
>>> input = 'foo\nfoofoo\n'.encode('utf-8')
>>> result = subprocess.run(cmd, stdout=subprocess.PIPE, input=input)
>>> result.stdout.decode('utf-8')
'foofoo\n'

stderr=subprocess.PIPE(capture to result.stderr) 또는 stderr=subprocess.STDOUT(capture to result.stdoutalong with regular output) 을 전달하여 오류를 캡처 할 수 있습니다 . 보안이 중요하지 않은 경우 shell=True아래 참고에 설명 된대로 전달 하여 더 복잡한 셸 명령을 실행할 수도 있습니다.

이것은 일을하는 기존 방식에 비해 약간의 복잡성을 추가합니다. 하지만 그만한 가치가 있다고 생각합니다. 이제 run함수만으로 필요한 거의 모든 것을 할 수 있습니다 .

이전 버전의 Python (2.7-3.4) : check_output

이전 버전의 Python을 사용 중이거나 적절한 하위 호환성이 필요한 경우 check_output위에서 간략하게 설명한대로 함수를 사용할 수 있습니다 . Python 2.7부터 사용할 수 있습니다.

subprocess.check_output(*popenargs, **kwargs)  

Popen(아래 참조) 와 동일한 인수를 취하고 프로그램의 출력을 포함하는 문자열을 반환합니다. 이 답변의 시작 부분에는 더 자세한 사용 예가 있습니다. 파이썬 3.5보다에서 check_output실행하는 것과 동일하다 run으로 check=True하고 stdout=PIPE, 바로 복귀 stdout속성.

당신은 통과 할 수 stderr=subprocess.STDOUT반환 된 출력에 포함되는 오류 메시지를 확인하기 위해 -하지만 파이썬은 전달의 일부 버전 stderr=subprocess.PIPEcheck_output캔 원인 교착 상태 . 보안이 중요하지 않은 경우 shell=True아래 참고에 설명 된대로 전달 하여 더 복잡한 셸 명령을 실행할 수도 있습니다.

stderr프로세스 에서 파이프 또는 입력을 전달 해야하는 경우 check_output작업을 수행 할 수 없습니다. Popen이 경우 아래 예를 참조하십시오 .

복잡한 애플리케이션 및 이전 버전의 Python (2.6 이하) : Popen

이전 버전과의 긴밀한 호환성이 필요하거나 check_output제공 하는 것보다 더 정교한 기능이 필요한 경우 Popen하위 프로세스에 대한 하위 수준 API를 캡슐화하는 개체를 직접 사용해야합니다 .

Popen생성자 하나 받아 하나의 명령 인수없이 또는 목록 인자의 번호리스트에 별도 항목으로서 각각 다음의 첫 번째 항목으로 명령을 포함한다. shlex.split문자열을 적절한 형식의 목록으로 구문 분석 할 수 있습니다. Popen개체는 또한 프로세스 IO 관리 및 저수준 구성을 위해 다양한 인수허용 합니다.

입력 및 캡처 출력을 보내려면 communicate거의 항상 선호되는 방법입니다. 에서와 같이 :

output = subprocess.Popen(["mycmd", "myarg"], 
                          stdout=subprocess.PIPE).communicate()[0]

또는

>>> import subprocess
>>> p = subprocess.Popen(['ls', '-a'], stdout=subprocess.PIPE, 
...                                    stderr=subprocess.PIPE)
>>> out, err = p.communicate()
>>> print out
.
..
foo

를 설정하면 stdin=PIPE, communicate또한이 과정을 통해 데이터를 전달 할 수 있습니다 stdin:

>>> cmd = ['awk', 'length($0) > 5']
>>> p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
...                           stderr=subprocess.PIPE,
...                           stdin=subprocess.PIPE)
>>> out, err = p.communicate('foo\nfoofoo\n')
>>> print out
foofoo

Aaron Hall의 대답에 유의하십시오 . 이는 일부 시스템 stdout에서 stderr, 및 stdin모두를 PIPE(또는 DEVNULL)로 설정해야 작업을 수행 할 수 있음을 나타냅니다 communicate.

드문 경우지만 복잡한 실시간 출력 캡처가 필요할 수 있습니다. Vartec 의 답변은 앞으로 나아갈 길을 제시하지만 communicate신중하게 사용하지 않으면 다른 방법 이 교착 상태에 빠지기 쉽습니다.

위의 모든 함수와 마찬가지로 보안이 문제가되지 않는 경우를 전달하여 더 복잡한 셸 명령을 실행할 수 있습니다 shell=True.

메모

1. 쉘 명령 실행 : shell=True인수

일반적으로 호출 할 때마다 run, check_output또는 Popen생성자는 실행 한 프로그램 . 이는 멋진 bash 스타일 파이프가 없음을 의미합니다. 복잡한 쉘 명령을 실행하려면 shell=True세 가지 함수가 모두 지원하는을 전달할 수 있습니다 .

그러나 그렇게하면 보안 문제가 발생 합니다. 가벼운 스크립팅 이상을 수행하는 경우 각 프로세스를 개별적으로 호출하고 각 프로세스의 출력을 다음을 통해 입력으로 전달하는 것이 좋습니다.

run(cmd, [stdout=etc...], input=other_output)

또는

Popen(cmd, [stdout=etc...]).communicate(other_output)

파이프를 직접 연결하려는 유혹이 강합니다. 저항하십시오. 그렇지 않으면, 당신은 가능성이 교착 상태를 참조하거나 같은 해키 일해야 할 것이다 .

2. 유니 코드 고려 사항

check_output파이썬 2에서는 문자열을 반환하지만 bytes파이썬 3 에서는 객체를 반환합니다. 유니 코드대해 배우지 않았다면 잠시 시간을내어 배울 가치가 있습니다.


이 방법이 더 쉽지만 Unix (Cygwin 포함) 및 Python2.7에서만 작동합니다.

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

(return_value, 출력)이있는 튜플을 반환합니다.

Python2와 Python3에서 모두 작동하는 솔루션의 경우 subprocess대신 모듈을 사용합니다.

from subprocess import Popen, PIPE
output = 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으로 리디렉션하고 있지만 정확히 원하는 것이 아닐 수도 있지만 오류 메시지도 원합니다.

이 함수는 결과를 줄 단위로 산출합니다 (일반적으로 전체 출력을 얻으려면 하위 프로세스가 완료 될 때까지 기다려야합니다).

귀하의 경우 사용법은 다음과 같습니다.

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

Vartec의 대답은 모든 줄을 읽지 않으므로 다음과 같은 버전을 만들었습니다.

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)

이것은 많은 상황에서 작동 하는 까다 롭지매우 간단한 솔루션입니다.

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

임시 파일 (여기서는 tmp)이 명령의 출력과 함께 생성되며 원하는 출력을 읽을 수 있습니다.

주석의 추가 참고 사항 : 일회성 작업의 경우 tmp 파일을 제거 할 수 있습니다. 이 작업을 여러 번 수행해야하는 경우 tmp를 삭제할 필요가 없습니다.

os.remove('tmp')

나는 똑같은 문제가 있었지만 이것을하는 매우 간단한 방법을 알아 냈습니다.

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

도움이되기를 바랍니다.

참고 :이 솔루션은 Python2 subprocess.getoutput()에서 작동하지 않으므로 Python3에만 해당됩니다.


다음 명령을 사용하여 모든 쉘 명령을 실행할 수 있습니다. 우분투에서 사용했습니다.

import os
os.popen('your command here').read()

참고 : 이것은 python 2.6부터 더 이상 사용되지 않습니다. 이제 subprocess.Popen. 아래는 예입니다

import subprocess

p = subprocess.Popen("Your command", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
print p.split("\n")

귀하의 마일리지가 다를 수 있습니다. Python 2.6.5의 Windows에서 Vartec 솔루션에 대해 @senderle의 스핀을 시도했지만 오류가 발생했고 다른 솔루션이 작동하지 않았습니다. 내 오류는 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]튜플의 첫 번째 요소를 가져옵니다. 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')

다음 요구 사항으로 동일한 문제에 대해 약간 다른 맛이 있습니다.

  1. STDOUT 버퍼에 누적되는 STDOUT 메시지를 캡처하고 반환합니다 (즉, 실시간).
    • @vartec은 위의 생성기와 'yield'
      키워드를 사용하여 Python 방식으로이 문제를 해결했습니다.
  2. 모든 STDOUT 행을 인쇄합니다 ( STDOUT 버퍼를 완전히 읽을 수 있기 전에 프로세스가 종료 된 경우에도 ).
  3. 고주파로 프로세스를 폴링하는 CPU 사이클을 낭비하지 마십시오.
  4. 하위 프로세스의 반환 코드 확인
  5. 0이 아닌 오류 반환 코드를 받으면 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)

여러 파일에 대해 셸 명령을 실행해야하는 경우이 방법이 도움이되었습니다.

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

편집 : 방금 JF Sebastian의 제안으로 Max Persson의 솔루션을 보았습니다. 앞서 가서 그것을 통합했습니다.


에 대한 초기 명령을 분할하는 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...'

shlex.split()코드가 없으면 다음과 같이 보일 것입니다.

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

@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에서 명령을 실행하는 것과 똑같이 작동합니다.


subprocesspython 모듈 을 사용하면 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

예 : 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 ""

The output can be redirected to a text file and then read it back.

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 __name__ == "__main__":
    path = "Somepath"
    command = 'ecls.exe /files ' + path
    print(execute(command))

참고URL : https://stackoverflow.com/questions/4760215/running-shell-command-and-capturing-the-output

반응형