여기의 이전 답변을 다소 확장하기 위해 일반적으로 간과되는 많은 세부 사항이 있습니다.
- 선호
subprocess.run()
이상 subprocess.check_call()
에 걸쳐 친구 subprocess.call()
이상 subprocess.Popen()
이상 os.system()
이상os.popen()
- 아마
text=True
일명 이해하고 사용하십시오 universal_newlines=True
.
- 의 의미를 이해
shell=True
하거나 shell=False
어떻게이 인용 변화와 쉘 편의 시설의 이용 가능성을.
sh
Bash 와의 차이점 이해
- 서브 프로세스가 상위 프로세스와 어떻게 다른지 이해하고 일반적으로 상위 프로세스를 변경할 수 없습니다.
- Python 인터프리터를 Python의 하위 프로세스로 실행하지 마십시오.
이러한 주제는 아래에보다 자세히 설명되어 있습니다.
선호 subprocess.run()
또는subprocess.check_call()
이 subprocess.Popen()
기능은 저급 인력이지만 올바르게 사용하기가 까다 롭고 여러 줄의 코드 복사 / 붙여 넣기를 끝내게됩니다 ... 다양한 목적을 위해 표준 라이브러리에 이미 높은 수준의 래퍼 함수 세트로 이미 존재합니다. 다음에 더 자세히 설명되어 있습니다.
다음은 설명서 의 단락입니다 .
서브 프로세스 호출에 권장되는 접근법 run()
은 처리 할 수있는 모든 사용 사례에 대해 기능 을 사용하는 것입니다. 고급 사용 사례의 경우 기본 Popen
인터페이스를 직접 사용할 수 있습니다.
불행히도 이러한 래퍼 함수의 가용성은 Python 버전마다 다릅니다.
subprocess.run()
파이썬 3.5에서 공식적으로 소개되었습니다. 다음을 모두 대체해야합니다.
subprocess.check_output()
Python 2.7 / 3.1에서 도입되었습니다. 기본적으로subprocess.run(..., check=True, stdout=subprocess.PIPE).stdout
subprocess.check_call()
파이썬 2.5에서 소개되었습니다. 기본적으로subprocess.run(..., check=True)
subprocess.call()
Python 2.4에서 원래 subprocess
모듈 ( PEP-324 ) 로 도입되었습니다 . 기본적으로subprocess.run(...).returncode
고급 API vs subprocess.Popen()
리팩토링 및 확장 된 subprocess.run()
기능은 기존 레거시 기능을 대체하는 것보다 훨씬 논리적이고 다양합니다. CompletedProcess
종료 상태, 표준 출력 및 완료된 하위 프로세스에서 몇 가지 다른 결과 및 상태 표시기를 검색 할 수있는 다양한 메소드 가있는 오브젝트를 리턴합니다 .
subprocess.run()
파이썬으로 제어를 실행하고 리턴하기 위해 단순히 프로그램이 필요하다면 갈 길입니다. 보다 복잡한 시나리오 (백그라운드 프로세스, Python 상위 프로그램의 대화식 I / O 사용)의 경우 여전히 subprocess.Popen()
모든 배관 작업 을 사용 하고 관리 해야합니다 . 이것은 모든 움직이는 부분을 상당히 복잡하게 이해해야하며 가볍게 수행해서는 안됩니다. 더 간단한 Popen
객체 는 (아직도 여전히 실행중인) 프로세스를 나타내며이 프로세스는 나머지 하위 프로세스 수명 동안 코드에서 관리해야합니다.
subprocess.Popen()
단지 프로세스 만 생성 한다는 점을 강조해야 합니다. 그것을 그대로두면 하위 프로세스가 Python과 함께 실행되므로 "백그라운드"프로세스가됩니다. 입력 또는 출력을 수행하거나 다른 방법으로 조정할 필요가 없으면 Python 프로그램과 함께 유용한 작업을 수행 할 수 있습니다.
피 os.system()
와os.popen()
영원한 이후 (파이썬 2.5부터) os
모듈 문서 에는 다음 subprocess
보다 선호하는 권장 사항이 포함되어 있습니다 os.system()
.
이 subprocess
모듈은 새로운 프로세스를 생성하고 결과를 검색 할 수있는보다 강력한 기능을 제공합니다. 이 기능을 사용하는 것보다 해당 모듈을 사용하는 것이 좋습니다.
문제 system()
는 분명히 시스템에 의존적이며 하위 프로세스와 상호 작용하는 방법을 제공하지 않는다는 것입니다. 파이썬의 범위를 벗어난 표준 출력 및 표준 오류와 함께 간단하게 실행됩니다. 파이썬이 수신하는 유일한 정보는 명령의 종료 상태입니다 (0은 성공을 의미하지만 0이 아닌 값의 의미는 다소 시스템에 따라 다릅니다).
PEP-324 (위에서 이미 언급 한)에는 os.system
문제가있는 이유 와 subprocess
이러한 문제를 해결하려는 시도에 대한보다 자세한 근거가 포함되어 있습니다.
os.popen()
더 강하게 낙담했었다 :
버전 2.6부터 사용되지 않습니다.이 기능은 더 이상 사용되지 않습니다. subprocess
모듈을 사용하십시오 .
그러나 Python 3에서 언젠가는을 사용하기 위해 다시 구현되어 세부 사항 subprocess
을 위해 subprocess.Popen()
설명서로 리디렉션됩니다 .
이해하고 일반적으로 사용 check=True
또한와 subprocess.call()
동일한 제한 사항이 많이 있음 을 알 수 있습니다 os.system()
. 정기적으로 사용, 당신은 일반적으로 프로세스가 성공적으로 완료 여부를 확인하는해야 subprocess.check_call()
하고 subprocess.check_output()
(후자는 완성 된 서브 프로세스의 표준 출력을 반환하는 경우) 할. 마찬가지로 서브 프로세스가 오류 상태를 리턴하도록 특별히 요구하지 않는 한 일반적으로 check=True
with 를 사용해야 subprocess.run()
합니다.
실제로,와 check=True
나 subprocess.check_*
, 파이썬은 발생합니다 CalledProcessError
예외를 하위 프로세스가 제로가 아닌 종료 상태를 반환합니다.
일반적인 오류 는 하위 프로세스가 실패한 경우 다운 스트림 코드가 실패 할 때 subprocess.run()
생략 check=True
하고 놀라는 것입니다.
반면에, 공통으로 문제 check_call()
와는 check_output()
경우 예외가 예를 제기 할 때 맹목적으로 이러한 기능을 사용하는 사용자가 놀랐다이었다 grep
일치하는 항목을 찾을 수 없습니다. ( grep
어쨌든 아래에 설명 된 것처럼 기본 Python 코드로 바꿔야 합니다.)
셀 수있는 모든 것, 쉘 명령이 종료 코드를 리턴하는 방법과 어떤 조건에서 0이 아닌 (오류) 종료 코드를 리턴하는지 이해하고 정확히 처리해야하는 방법을 신중하게 결정해야합니다.
text=True
일명 이해하고 아마 사용universal_newlines=True
Python 3부터 Python 내부의 문자열은 유니 코드 문자열입니다. 그러나 서브 프로세스가 유니 코드 출력 또는 문자열을 생성한다는 보장은 없습니다.
(차이가 분명하지 않으면 Ned Batchelder의 Pragmatic Unicode 를 권장합니다. 읽기는 의무적이지 않지만 읽기를 원할 경우 링크 뒤에 36 분짜리 비디오 프레젠테이션이 있지만 페이지를 읽는 데는 시간이 훨씬 적게 걸립니다. )
깊이 내려 가면 파이썬은 bytes
버퍼 를 가져와 어떻게 든 해석해야합니다. 이진 데이터가 포함되어 있으면 오류가 발생하기 쉽고 버그를 유발하는 행동이기 때문에 유니 코드 문자열로 디코딩 해서는 안됩니다 . 인코딩 된 텍스트와 이진 데이터를 올바르게 구분합니다.
을 사용하면 text=True
실제로 시스템의 기본 인코딩에서 텍스트 데이터를 다시 기대할 수 있으며 Python의 능력을 최대한 발휘할 수 있도록 Python (유니 코드) 문자열로 디코딩해야합니다. 아마도 Windows를 제외한 날짜 시스템?)
그것이 다시 요청 하지 않으면 파이썬은 bytes
문자열 stdout
과 stderr
문자열을 제공합니다. 어쩌면 일부에서 나중에 지적 할 그들이 텍스트 문자열이 결국 있다고 알고, 당신은 자신의 인코딩을 알고있다. 그런 다음 해독 할 수 있습니다.
normal = subprocess.run([external, arg],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True,
text=True)
print(normal.stdout)
convoluted = subprocess.run([external, arg],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True)
# You have to know (or guess) the encoding
print(convoluted.stdout.decode('utf-8'))
Python 3.7 text
은 이전에 다소 오도 된 키워드 인수에 대해 더 짧고 이해하기 쉽고 이해하기 쉬운 별명 을 도입했습니다 universal_newlines
.
이해 shell=True
대shell=False
함께 shell=True
하면 쉘에 하나의 문자열을 전달하고, 쉘은 거기에서 걸립니다.
함께 shell=False
하면 쉘을 우회 OS에 인수 목록을 전달합니다.
쉘이없는 경우 프로세스를 저장하고 상당히 많은 양의 숨겨진 복잡성을 제거하여 버그 나 보안 문제가있을 수 있습니다.
반면에 쉘이없는 경우 리디렉션, 와일드 카드 확장, 작업 제어 및 기타 여러 가지 쉘 기능이 없습니다.
일반적인 실수는 shell=True
파이썬 을 사용 하여 토큰 목록을 전달하는 것입니다. 이것은 어떤 경우에는 효과가 있지만 실제로는 잘못 정의되어 있으며 흥미로운 방식으로 깨질 수 있습니다.
# XXX AVOID THIS BUG
buggy = subprocess.run('dig +short stackoverflow.com')
# XXX AVOID THIS BUG TOO
broken = subprocess.run(['dig', '+short', 'stackoverflow.com'],
shell=True)
# XXX DEFINITELY AVOID THIS
pathological = subprocess.run(['dig +short stackoverflow.com'],
shell=True)
correct = subprocess.run(['dig', '+short', 'stackoverflow.com'],
# Probably don't forget these, too
check=True, text=True)
# XXX Probably better avoid shell=True
# but this is nominally correct
fixed_but_fugly = subprocess.run('dig +short stackoverflow.com',
shell=True,
# Probably don't forget these, too
check=True, text=True)
일반적인 레토르트 "하지만 그것은 나를 위해 작동합니다"는 당신이 그것이 어떤 상황에서 작동을 멈출 수 있는지 정확히 이해하지 않는 한 유용한 반박이 아닙니다.
리팩토링 예제
종종 쉘의 기능을 원시 파이썬 코드로 대체 할 수 있습니다. Simple Awk 또는 sed
스크립트는 아마도 단순히 Python으로 변환되어야합니다.
이것을 부분적으로 설명하기 위해 많은 쉘 기능을 포함하는 일반적이지만 약간 어리석은 예가 있습니다.
cmd = '''while read -r x;
do ping -c 3 "$x" | grep 'round-trip min/avg/max'
done <hosts.txt'''
# Trivial but horrible
results = subprocess.run(
cmd, shell=True, universal_newlines=True, check=True)
print(results.stdout)
# Reimplement with shell=False
with open('hosts.txt') as hosts:
for host in hosts:
host = host.rstrip('\n') # drop newline
ping = subprocess.run(
['ping', '-c', '3', host],
text=True,
stdout=subprocess.PIPE,
check=True)
for line in ping.stdout.split('\n'):
if 'round-trip min/avg/max' in line:
print('{}: {}'.format(host, line))
여기에 참고할 사항 :
- 로
shell=False
쉘이 문자열을 주변에 요구하는 당신에게 인용을 필요로하지 않습니다. 어쨌든 따옴표를 넣는 것은 아마도 오류 일 것입니다.
- 하위 프로세스에서 가능한 한 적은 코드를 실행하는 것이 좋습니다. 이를 통해 Python 코드 내에서 실행을보다 효과적으로 제어 할 수 있습니다.
- 그러나 복잡한 쉘 파이프 라인은 지루하고 때로는 파이썬에서 다시 구현하기가 어렵습니다.
리팩토링 된 코드는 셸이 매우 간결한 구문을 사용하여 실제로 얼마나 많은 일을하는지 보여줍니다. 파이썬은 명시 적이 암시 적보다 낫다고 말하지만 파이썬 코드 는 다소 장황하며 실제로 이것보다 더 복잡해 보입니다. 반면에 쉘 명령 출력과 함께 호스트 이름을 쉽게 포함시킬 수있는 기능 향상으로 사소한 부분을 제어 할 수있는 여러 지점을 제공합니다. (이것은 쉘에서 수행하는 데 결코 도전이 아니며, 또 다른 전환과 다른 프로세스를 희생하여 발생합니다.)
일반적인 쉘 구조
완전성을 위해 다음은 이러한 쉘 기능 중 일부에 대한 간략한 설명과 기본 Python 기능으로 대체 할 수있는 방법에 대한 참고 사항입니다.
- 글 로빙 일명 와일드 카드 확장은와
glob.glob()
같은 간단한 파이썬 문자열 비교 로 대체 하거나 대체 할 수 있습니다 for file in os.listdir('.'): if not file.endswith('.png'): continue
. Bash에는 .{png,jpg}
괄호 확장 및 {1..100}
물결표 확장과 같은 다양한 기타 확장 기능 ~
이 있습니다 (홈 디렉토리로, 더 일반적으로 ~account
는 다른 사용자의 홈 디렉토리로 확장 )
- 쉘 변수 는 파이썬 변수와 유사
$SHELL
하거나 $my_exported_var
때로는 간단하게 대체 될 수 있습니다. 예를 들어 같은 수출 쉘 변수를 사용할 수 있습니다 os.environ['SHELL']
(의 의미가 export
하위 프로세스에 변수를 사용할 수 있도록하는 것입니다 -. 분명히 파이썬은 쉘의 서브 프로세스, 또는 반대의 부사장으로 실행에 사용할 수 없습니다 서브 프로세스에 사용할 수없는 변수 env=
키워드 subprocess
메소드에 대한 인수를 사용하면 서브 프로세스의 환경을 사전으로 정의 할 수 있으므로 서브 프로세스에 Python 변수를 표시 할 수 있습니다. 함께 shell=False
하면 어떤 따옴표를 제거하는 방법을 이해해야합니다; 예를 들어, 디렉토리 이름을 따옴표없이 사용 cd "$HOME"
하는 것과 같습니다 os.chdir(os.environ['HOME'])
. (매우 자주cd
어쨌든 유용하거나 필요하지 않으며 많은 초보자는 변수 주위에 큰 따옴표를 생략하고 하루까지 그 단어를 사용하지 않습니다 ... )
- 리디렉션을 사용하면 파일에서 표준 입력으로 읽고 표준 출력을 파일에 쓸 수 있습니다. 쓰기 및 읽기를 위해
grep 'foo' <inputfile >outputfile
열리고 해당 내용을 표준 입력으로 전달합니다 . 표준 출력은에 입력됩니다 . 일반적으로 네이티브 Python 코드로 대체하기는 어렵지 않습니다.outputfile
inputfile
grep
outputfile
- 파이프 라인은 일종의 리디렉션입니다.
echo foo | nl
두 개의 하위 프로세스를 실행합니다. 여기서 표준 출력은 echo
표준 입력입니다 nl
(OS 수준에서 Unix 계열 시스템에서는 단일 파일 핸들 임). 파이프 라인의 한쪽 끝이나 양쪽 끝을 네이티브 파이썬 코드로 대체 할 수없는 경우, 특히 파이프 라인에 2-3 개 이상의 프로세스가 pipes
있는 경우 (파이썬 표준 라이브러리 의 모듈 이나 숫자를 보더라도) 쉘 사용을 고려하십시오. 보다 현대적이고 다양한 타사 경쟁 업체).
- 작업 제어를 통해 작업을 중단하고 백그라운드에서 실행하고 포 그라운드로 되돌릴 수 있습니다. 프로세스를 중지하고 계속하기위한 기본 Unix 신호도 물론 Python에서도 사용할 수 있습니다. 그러나 작업은 쉘에서 상위 레벨 추상화로, 프로세스 그룹 등이 포함됩니다. 파이썬에서 이와 같은 작업을 수행하려면 이해해야합니다.
- 쉘에서 인용하는 것은 모든 것이 기본적으로 문자열 이라는 것을 이해할 때까지 혼란 스러울 수 있습니다 . 그래서
ls -l /
에 해당 'ls' '-l' '/'
하지만, 주위에 인용 리터럴은 완전히 선택 사항입니다. 쉘 메타 문자를 포함하는 따옴표없는 문자열에는 매개 변수 확장, 공백 토큰 화 및 와일드 카드 확장이 적용됩니다. 큰 따옴표는 공백 토큰 화 및 와일드 카드 확장을 방지하지만 매개 변수 확장 (변수 대체, 명령 대체 및 백 슬래시 처리)을 허용합니다. 이론 상으로는 간단하지만 특히 여러 계층의 해석 (예 : 원격 셸 명령)이있는 경우 당황 할 수 있습니다.
sh
Bash 와의 차이점 이해
subprocess
/bin/sh
특별히 다르게 요청하지 않는 한 쉘 명령을 실행합니다 (물론 Windows에서 COMSPEC
변수 값을 사용하는 경우는 제외 ). 것을이 수단 다양한 배쉬 전용 배열 같은 기능, [[
등은 사용할 수 없습니다.
Bash 전용 구문을 사용해야하는 경우 셸 경로를 다음과 같이 전달할 수 있습니다 executable='/bin/bash'
(물론 Bash가 다른 곳에 설치된 경우 경로를 조정해야합니다).
subprocess.run('''
# This for loop syntax is Bash only
for((i=1;i<=$#;i++)); do
# Arrays are Bash-only
array[i]+=123
done''',
shell=True, check=True,
executable='/bin/bash')
A subprocess
는 부모와 분리되어 있으며 변경할 수 없습니다
다소 일반적인 실수는 다음과 같은 일을하는 것입니다.
subprocess.run('foo=bar', shell=True)
subprocess.run('echo "$foo"', shell=True) # Doesn't work
우아함의 부족을 제외하고는 "서브 프로세스"라는 이름의 "서브"부분에 대한 이해의 근본적인 부족을 배신한다.
자식 프로세스는 파이썬과 완전히 별개로 실행되며, 끝날 때 파이썬은 종료 상태에서 유추하여 자식 프로세스의 출력을 유추 할 수있는 모호한 표시기 외에는 무엇을했는지 전혀 모릅니다. 아이는 일반적으로 부모의 환경을 바꿀 수 없습니다. 변수를 설정하거나 작업 디렉토리를 변경하거나 많은 경우 부모의 협조없이 부모와 통신 할 수 없습니다.
이 특정한 경우의 즉각적인 수정은 단일 하위 프로세스에서 두 명령을 모두 실행하는 것입니다.
subprocess.run('foo=bar; echo "$foo"', shell=True)
분명히이 특별한 유스 케이스에는 쉘이 전혀 필요하지 않습니다. 다음을 통해 현재 프로세스 (및 그 자식)의 환경을 조작 할 수 있습니다.
os.environ['foo'] = 'bar'
또는 환경 설정을 사용하여 하위 프로세스에 환경 설정을 전달하십시오.
subprocess.run('echo "$foo"', shell=True, env={'foo': 'bar'})
(명백한 리팩토링을 언급하지 않기 위하여 subprocess.run(['echo', 'bar'])
,하지만 echo
실행에 무언가의 빈약 한 예는 물론, 처음부터 하위 프로세스에있다).
파이썬에서 파이썬을 실행하지 마십시오
이것은 약간 모호한 조언입니다. 파이썬 인터프리터를 파이썬 스크립트의 서브 프로세스로 실행해야하는 상황이 있거나 심지어는 절대적으로 필요한 상황도 있습니다. 그러나 종종 올바른 접근 방식은 단순히 import
다른 파이썬 모듈이 호출 스크립트에 들어가 함수를 직접 호출하는 것입니다.
다른 파이썬 스크립트가 당신의 통제하에 있고 그것이 모듈이 아닌 경우, 하나로 바꾸는 것을 고려 하십시오 . (이 답변은 이미 너무 길기 때문에 여기서 자세히 다루지 않겠습니다.)
병렬 처리가 필요한 경우 multiprocessing
모듈을 사용하여 하위 프로세스에서 Python 함수를 실행할 수 있습니다 . threading
단일 프로세스에서 여러 작업을 실행하는 작업 도 있습니다 (더 가볍고 더 많은 제어 기능을 제공하지만 프로세스 내의 스레드가 밀접하게 결합되어 단일 GIL에 바인딩되어 있음 ).
cwm
..bashrc
대화식 bash 사용을 위해 환경을 설정하는 구성이 있습니까?