python subprocess.call ()이 예상대로 작동하지 않습니다


11

파이썬에서 설정 스크립트를 만드는 방법에 익숙해지기 위해이 토끼 구멍을 시작했습니다. 파이썬의 선택은 단순히 이에 익숙하다는 데 뿌리를두고 있지만이 작업에는 파이썬보다 더 나은 대안이있을 것이라고 확신합니다.

이 스크립트의 목표는 스크립트를 실행하는 머신에 ROS를 설치하고 catkin 환경을 설정하는 것입니다. 방향은 여기여기 에서 각각 찾을 수 있습니다 .

현재 스크립트는 다음과 같습니다.

subprocess.call(["sudo", "sh", "-c", "'echo \"deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main\" > /etc/apt/sources.list.d/ros-latest.list'"])
subprocess.call(["sudo", "apt-key", "adv", "--keyserver", "hkp://ha.pool.sks-keyserver.net:80", "--recv-key", "0xB01FA116"])
subprocess.call(["sudo", "apt-get", "update"])
subprocess.call(["sudo", "apt-get", "install", "ros-kinetic-desktop-full", "-y"])
subprocess.call(["sudo", "rosdep", "init"])
subprocess.call(["rosdep", "update"])
subprocess.call(["echo", '"source /opt/ros/kinetic/setup.bash"', ">>", "~/.bashrc", "source", "~/.bashrc"])
subprocess.call(["sudo", "apt-get", "install", "python-rosinstall", "-y"])
mkdir_p(os.path.expanduser('~') + "/catkin_ws/src")
subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"])
subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws && catkin_make)"])
subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws && source devel/setup.bash"])

스크립트가 현재 실행 중이면 오류와 함께 오류가 발생합니다.

Traceback (most recent call last):
  File "setup.py", line 46, in <module>
    subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"])
  File "/usr/lib/python2.7/subprocess.py", line 523, in call
    return Popen(*popenargs, **kwargs).wait()
  File "/usr/lib/python2.7/subprocess.py", line 711, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1343, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory

터미널 창에서 수동으로 실행할 때 명령이 올바르게 작동하는지 확인했으며, 이것이이 스크립트와 해당 범위가 OS 내에서 처리되는 방식에 대한 근본적인 오해라고 생각합니다. 많은 혼란을 초래하는 부분은 제공된 디렉토리를 찾을 수 없다고 불평하는 이유입니다.이 디렉토리가 존재하는지 확인했습니다. 파이썬에서 명령을 인쇄하고 터미널 창에 붙여 넣을 때 오류가 발생하지 않습니다.


파이썬에는 자체 기능이 있습니다os.chdir()
Jacob Vlijm

1
파이썬 3를 사용하는 경우, 그냥 통과 cwd에 인수call
intsco

답변:


18

기본적으로 subprocess.call명령을 실행하기 위해 쉘을 사용하지 않으므로 다음과 같은 명령을 쉘 할 수 없습니다 cd.

쉘을 사용하여 명령을 실행하려면 shell=True매개 변수로 사용하십시오 . 이 경우 명령을 목록이 아닌 단일 문자열로 전달하는 것이 좋습니다. 그리고 쉘에서 실행 ~/되므로 경로에서도 사용할 수 있습니다 .

subprocess.call("(cd ~/catkin_ws/src && catkin_make)", shell=True)

1
감사합니다! 나는 subprocess.call이 쉘을 사용했다는 인상을 받았으며 명시 적으로 언급해야한다는 것을 알지 못했습니다. 위의 명령은 의도 한대로 정확하게 작동했습니다
beeedy

1
왜 사용하지 os.chdir()않습니까?
Jacob Vlijm

3
어때요 subprocess.call(['catkin_make'], cwd=os.path.expanduser('~/catkin_ws/src'))?
Matt Nordhoff

shell=True기본 쉘인 대시를 호출합니다. OP가있는 스크립트에 bashism이 포함되어 있으면 스크립트가 중단 될 수 있습니다. 내 답변에 편집을 추가했습니다. 대체 솔루션은 명시 적으로 특정 쉘을 호출하는 것입니다. 누군가 csh 스크립트를 다루는 경우에 특히 유용합니다
Sergiy Kolodyazhnyy

1
가장 좋은 해결책은 Matt Nordhoff의 제안입니다. 고정 명령을 사용 shell=True 하더라도 보안 취약점이 발생합니다 (예 : 취약한 시스템에서 쉘 쇼크가 트리거 될 수 있음). 엄지 손가락의 규칙 : 사용 피할 수 있다면 shell=True당신이 해야 그것을 피할. 이 cwd매개 변수는 OP가 원하는 종류의 호출을 정확하게 수행합니다.
Bakuriu

5

subprocess.call() 첫 번째 항목이 분명히 합법적 인 쉘 명령 인 목록을 기대합니다. 예를 들어 이것을 비교하십시오.

>>> subprocess.call(['echo hello'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/subprocess.py", line 523, in call
    return Popen(*popenargs, **kwargs).wait()
  File "/usr/lib/python2.7/subprocess.py", line 711, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1343, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory
>>> subprocess.call(['echo', 'hello'])
hello
0

귀하의 경우에는 subprocess.call(["(cd "+ os.path.expanduser('~') + "/catkin_ws/src)"])다음과 같은 이진을 찾을 것으로 예상됩니다 (공간 문자를 지정하는 백 슬래시 참고).

 cd\ /home/user/catkin_ws/src

이는 시스템 어딘가에있을 것으로 예상되는 하나의 단일 이름으로 취급됩니다. 당신이 정말로하고 싶은 것은 :

 subprocess.call(["cd", os.path.expanduser('~') + "/catkin_ws/src"])

서브 쉘을 사용할 이유가 없기 때문에 쉼표 주위의 괄호를 제거했습니다.

편집 :

그러나이 경우에는 사용 cd이 중복 된다는 의견에서 프로 고에 의해 이미 언급되었습니다 . Florian의 답변은 subprocess.call()쉘을 사용하지 않는 것을 적절하게 언급합니다 . 두 가지 방법으로 접근 할 수 있습니다. 하나, 당신은 사용할 수 있습니다subprocess.call("command string",shell=True)

다른 방법은 특정 쉘을 명시 적으로 호출하는 것입니다. 특정 쉘이 필요한 스크립트를 실행하려는 경우 특히 유용합니다. 따라서 당신은 할 수 있습니다 :

subprocess.call(['bash' , os.path.expanduser('~')  + "/catkin_ws/src"  ) ] )

1
call()합법적 인 쉘 명령을 기대하지 않습니다. 실제 실행 파일의 경로를 찾을 것으로 예상합니다. 그리고 독립를 호출하면 cd아무것도 달성하지 않습니다 다음 CWD는 프로세스가 종료되면 존재 중단 프로세스 특정 변수입니다.
nperson325681

@progo good point, 나는 OP의 명령을 편집하는 데 너무 집중하여 cd여기서 아무것도 하지 않을 것조차 알지 못했습니다 . . . . 그러나 "합법적 인"에 관해서는, 여전히 믿습니다. 제가 subprocess.call()찾을 수없는 것을 주면 ['ls -l'] 합법적이지 않습니다
Sergiy Kolodyazhnyy

@progo 작은 편집을 검토하십시오
Sergiy Kolodyazhnyy

3

os.chdir()대신 사용하십시오 .

기존 답변에서 언급 한 문제 외에도 디렉토리를 변경하기 위해을 사용 shell=True하거나 subprocess.call()여기를 사용하는 것을 선호하지 않습니다 .

파이썬에는 디렉토리를 변경하는 자체 방법이 있습니다 os.chdir()(잊지 마세요 import os). ~( "home")은 여러 가지 방법으로 정의 할 수 있습니다 (ao) os.environ["HOME"].

over를 선호하는 이유는 여기서shell=True 읽을 수 있습니다 .


0

참고 사용하여 해당 os.chdir()하는 경우, 예를 의도하지 않은 부작용을 일으킬 수 있습니다 당신이 멀티 스레딩을 사용하고 있습니다 . subprocess메소드는 모두 cwd파이썬 프로세스의 다른 부분에 영향을 미치지 않고 해당 디렉토리에서 요청 된 하위 프로세스를 실행 하는 키워드 인수를 제공합니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.