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

ฉันตรวจสอบแล้วว่าคำสั่งทำงานได้อย่างถูกต้องเมื่อดำเนินการด้วยตนเองจากหน้าต่างเทอร์มินัลและฉันเชื่อว่านี่เป็นความเข้าใจผิดขั้นพื้นฐานเกี่ยวกับวิธีจัดการสคริปต์นี้และขอบเขตของสคริปต์ภายในระบบปฏิบัติการ ส่วนที่ทำให้ฉันสับสนเป็นจำนวนมากคือสาเหตุที่มันบ่นว่าไม่สามารถค้นหาไดเรกทอรีที่ให้มาได้ในขณะที่ฉันตรวจสอบแล้วว่ามีไดเรกทอรีนี้อยู่ เมื่อคำสั่งถูกพิมพ์จาก python และวางลงในหน้าต่างเทอร์มินัลจะไม่พบข้อผิดพลาด


Python มีของตัวเองos.chdir()
Jacob Vlijm

1
หากคุณใช้ Python 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 มี bashisms มันอาจแตก ฉันได้เพิ่มการแก้ไขในคำตอบของฉันทางเลือกอื่นจะเรียกเปลือกเฉพาะอย่างชัดเจน มีประโยชน์อย่างยิ่งถ้ามีใครบางคนกำลังจัดการกับสคริปต์ csh
Sergiy Kolodyazhnyy

1
ทางออกที่ดีที่สุดคือข้อเสนอแนะของ Matt Nordhoff การใช้shell=True แม้กับคำสั่งคงที่จะเปิดช่องโหว่ด้านความปลอดภัย (เช่น shellshock สามารถถูกเรียกใช้ในระบบที่มีช่องโหว่) กฎง่ายๆ: ถ้าคุณสามารถหลีกเลี่ยงการใช้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)"])คาดว่าจะหาไบนารีที่ดูเหมือนดังนั้น (หมายเหตุเครื่องหมายแบ็กสแลชที่กำหนด charater พื้นที่):

 cd\ /home/user/catkin_ws/src

ที่ถือว่าเป็นชื่อเดียวที่คาดว่าจะอยู่ที่ไหนสักแห่งในระบบของคุณ สิ่งที่คุณอยากทำคือ:

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

โปรดทราบว่าฉันได้ลบวงเล็บรอบเครื่องหมายจุลภาคเนื่องจากไม่มีเหตุผลที่จะใช้ subshell

แก้ไข :

แต่ได้รับการกล่าวถึงแล้วโดย progo ในความคิดเห็นที่ใช้cdในกรณีนี้ซ้ำซ้อน คำตอบของฟลอเรียนยังระบุอย่างถูกต้องsubprocess.call()ว่าไม่ได้ใช้เปลือกหอย คุณสามารถเข้าใกล้ได้สองวิธี หนึ่งคุณสามารถใช้subprocess.call("command string",shell=True)

อีกวิธีหนึ่งคือการเรียกเปลือกเฉพาะอย่างชัดเจน สิ่งนี้มีประโยชน์อย่างยิ่งหากคุณต้องการเรียกใช้สคริปต์ที่ต้องการเชลล์เฉพาะ ดังนั้นคุณสามารถทำได้:

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

1
call()ไม่คาดหวังคำสั่งเชลล์ที่ถูกต้อง; มันคาดว่าจะหาเส้นทางไปยังปฏิบัติการจริง และการเรียกใช้งานแบบสแตนด์อโลนcdไม่ประสบความสำเร็จอะไร: CWD เป็นตัวแปรเฉพาะของกระบวนการที่หยุดอยู่เมื่อกระบวนการออกจากกระบวนการ
nperson325681

@progo จุดที่ดีฉันจึงมุ่งเน้นไปที่การแก้ไขคำสั่งของ OP ที่ฉันไม่ได้สังเกตเห็นว่าcdจะไม่ทำอะไรที่นี่ . . . แต่สำหรับ "ถูกกฎหมาย" มันยังคงเป็นวลีที่เหมาะสมที่ฉันเชื่อ - ถ้าฉันให้subprocess.call()บางสิ่งบางอย่างที่ไม่สามารถหาได้เช่น['ls -l'] มันจะไม่ถูกต้องตามกฎหมาย
Sergiy Kolodyazhnyy

@progo ทำการแก้ไขเล็กน้อยโปรดตรวจสอบ
Sergiy Kolodyazhnyy

3

ใช้os.chdir()แทน

นอกเหนือจากปัญหาที่กล่าวถึงในคำตอบที่มีอยู่ฉันไม่ต้องการใช้shell=Trueหรือsubprocess.call()ที่นี่เพื่อเปลี่ยนไดเรกทอรี

Python มีวิธีการเปลี่ยนไดเรกทอรีเป็นของตัวเองos.chdir()(อย่าลืมimport os) ~( "บ้าน") os.environ["HOME"]สามารถกำหนดได้ในหลายวิธีอ่าว

เหตุผลที่ชอบมากกว่าshell=Trueนั้นสามารถอ่านได้ที่นี่


0

หมายเหตุว่าการใช้os.chdir()อาจทำให้เกิดความไม่ตั้งใจผลข้างเคียงเช่นถ้าคุณกำลังใช้มัลติเธรด subprocessเมธอดทั้งหมดจัดเตรียมcwdอาร์กิวเมนต์คำหลักที่จะเรียกใช้กระบวนการย่อยที่ร้องขอในไดเรกทอรีนั้นโดยไม่มีผลกระทบต่อส่วนอื่น ๆ ของกระบวนการหลามของคุณ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.