2009-08-10 14 views
19

जब subprocess.Popen(args, shell=True) का उपयोग कर "gcc --version" (सिर्फ एक उदाहरण के रूप में) को चलाने के लिए, विंडोज पर हम इस मिल:क्यों subprocess.Popen() खोल के साथ = सही काम लिनक्स बनाम विंडोज पर अलग है?

>>> from subprocess import Popen 
>>> Popen(['gcc', '--version'], shell=True) 
gcc (GCC) 3.4.5 (mingw-vista special r3) ... 

तो यह अच्छी तरह से संस्करण की छपाई के रूप में मैं उम्मीद करते हैं। लेकिन लिनक्स पर हम इस मिल:

>>> from subprocess import Popen 
>>> Popen(['gcc', '--version'], shell=True) 
gcc: no input files 

क्योंकि जीसीसी --version विकल्प नहीं मिला है।

दस्तावेज़ यह निर्दिष्ट नहीं करते हैं कि विंडोज के तहत तर्कों के साथ क्या होना चाहिए, लेकिन यह यूनिक्स, पर कहता है, "अगर तर्क एक अनुक्रम है, तो पहला आइटम कमांड स्ट्रिंग निर्दिष्ट करता है, और कोई अतिरिक्त आइटम होगा अतिरिक्त खोल तर्क के रूप में माना जाता है। " आईएमएचओ विंडोज़ तरीका बेहतर है, क्योंकि यह आपको Popen(arglist) को Popen(arglist, shell=True) वाले के रूप में कॉल करने की अनुमति देता है।

विंडोज और लिनक्स के बीच अंतर क्यों है?

+0

आमतौर पर पाइथन का संस्करण शामिल करने का एक अच्छा विचार है या कम से कम यदि यह लाइन 2 या 3 है। – mloskot

उत्तर

14

विंडोज पर वास्तव में, यह cmd.exe का उपयोग करते हैं shell=True करता है - यह पहले जोड़ता खोल तर्कों को cmd.exe /c (यह वास्तव में ऊपर COMSPEC वातावरण चर लेकिन चूक cmd.exe करने के लिए यदि मौजूद नहीं दिखता है)। (विंडोज 95/98 पर यह वास्तव में कमांड लॉन्च करने के लिए इंटरमीडिएट w9xpopen प्रोग्राम का उपयोग करता है)।

तो अजीब कार्यान्वयन वास्तव में UNIX है, जो करता है निम्नलिखित (जहां प्रत्येक अंतरिक्ष एक अलग तर्क अलग करती है):

/bin/sh -c gcc --version 

यह सही कार्यान्वयन (कम से कम लिनक्स पर) की तरह लग रहा होगा:

/bin/sh -c "gcc --version" gcc --version 

चूंकि यह उद्धृत पैरामीटर से कमांड स्ट्रिंग सेट करेगा, और अन्य पैरामीटर सफलतापूर्वक पास करेगा।

-c के लिए

sh आदमी पृष्ठ अनुभाग से:

--- subprocess.py.orig 2009-04-19 04:43:42.000000000 +0200 
+++ subprocess.py  2009-08-10 13:08:48.000000000 +0200 
@@ -990,7 +990,7 @@ 
       args = list(args) 

      if shell: 
-    args = ["/bin/sh", "-c"] + args 
+    args = ["/bin/sh", "-c"] + [" ".join(args)] + args 

      if executable is None: 
       executable = args[0] 
+0

यह बहुत अच्छा है, डेविड धन्यवाद। मैं सही कार्यान्वयन के बारे में सहमत हूं और आपका पैच अच्छा दिखता है। क्या आप एक पाइथन बग रिपोर्ट सबमिट करने की तुलना में एक (बेहतर) स्थिति में हैं - दूसरे शब्दों में, क्या आपने पहले ऐसा किया है, या मैं इसे देखूँगा? –

+1

जोड़ा गया http://bugs.python.org/issue6689 - अच्छा होगा अगर आप इसका अनुसरण कर सकें, वहां टिप्पणी करें आदि –

+0

धन्यवाद! मैंने खुद को नोजी सूची में जोड़ा है। –

5

subprocess.py स्रोत से:

यूनिक्स पर, खोल के साथ = सच: यदि आर्ग एक स्ट्रिंग है, यह खोल के माध्यम से निष्पादित करने के लिए कमांड स्ट्रिंग निर्दिष्ट करता है। अगर तर्क एक अनुक्रम है, पहला आइटम कमांड स्ट्रिंग निर्दिष्ट करता है, और कोई भी अतिरिक्त आइटम अतिरिक्त शैल तर्क के रूप में माना जाएगा।

विंडोज़ पर: पॉपन क्लास प्रोग्राम निष्पादित करने के लिए CreateProcess() का उपयोग करता है, जो तारों पर चलती है। अगर तर्क एक अनुक्रम है, तो यह सूची 2cmdline विधि का उपयोग कर स्ट्रिंग में परिवर्तित हो जाएगा। कृपया ध्यान दें कि सभी एमएस विंडोज अनुप्रयोग कमांड लाइन को उसी तरीके से समझते हैं: list2cmdline को उसी नियमों का उपयोग एमएस सी रनटाइम के रूप में अनुप्रयोगों के लिए डिज़ाइन किया गया है।

यह उत्तर नहीं देता है कि, यह स्पष्ट करता है कि आप अपेक्षित व्यवहार देख रहे हैं।

"क्यों" शायद यूनिक्स जैसी प्रणालियों पर है, कमांड तर्क वास्तव में अनुप्रयोगों के माध्यम से पारित होते हैं (exec* कॉल के परिवार का उपयोग करके) तारों की एक सरणी के रूप में। दूसरे शब्दों में, कॉलिंग प्रक्रिया तय करता है कि EACH कमांड लाइन तर्क में क्या होता है। जबकि जब आप इसे खोल का उपयोग करने के लिए कहते हैं, तो कॉलिंग प्रक्रिया वास्तव में केवल एक कमांड लाइन तर्क को निष्पादित करने के लिए खोलने का मौका देती है: संपूर्ण कमांड लाइन जिसे आप निष्पादित करना चाहते हैं, निष्पादन योग्य नाम और तर्क, एक स्ट्रिंग के रूप में।

लेकिन विंडोज़ पर, संपूर्ण कमांड लाइन (उपर्युक्त दस्तावेज के अनुसार) को बाल प्रक्रिया में एक स्ट्रिंग के रूप में पास किया जाता है। यदि आप CreateProcess एपीआई दस्तावेज देखते हैं, तो आप देखेंगे कि यह सभी कमांड लाइन तर्कों को एक बड़ी स्ट्रिंग में एक साथ संयोजित करने की अपेक्षा करता है (इसलिए list2cmdline पर कॉल करें)।

प्लस वहाँ तथ्य यह है कि वहाँ यूनिक्स सिस्टम पर वास्तव में है एक खोल उपयोगी कर सकते हैं कि है, इसलिए मुझे लगता है कि अंतर के लिए अन्य कारण यह है कि विंडोज पर, shell=True कुछ भी नहीं यही वजह है कि है करता है, है यह जिस तरह से आप देख रहे हैं काम कर रहा है।विंडोज़ पर shell=True पर दो सिस्टम को समान रूप से कार्य करने का एकमात्र तरीका यह होगा कि जब आप shell=True पर सभी कमांड लाइन तर्कों को छोड़ दें।

+1

विंडोज़ पर भी एक खोल है (आमतौर पर 'cmd.exe'), लेकिन आपके द्वारा उद्धृत टिप्पणी उपर्युक्त इंगित करता है कि पाइथन वास्तव में इसका उपयोग नहीं करता है जब खोल = सही (इसके बजाय, यह 'CreateProcess() 'सीधे उपयोग करता है)। –

+1

धन्यवाद - जैसा कि ग्रेग ने उल्लेख किया है, निश्चित रूप से विंडोज़ (cmd.exe या COMSPEC में से एक) पर एक खोल है। और इसका उपयोग पॉपन द्वारा किया जाता है (हालांकि CreateProcess के माध्यम से) - subprocess.py स्रोत देखें। तो यह अभी भी मुझे लगता है कि पोर्टेबिलिटी pitfalls से बचने के लिए subprocess उन्हें एक ही तरह से काम करना चाहिए ... –

+0

नोट: [दस्तावेज़ 2010 से अद्यतन किया गया है] (https://docs.python.org/3/library /subprocess.html#popen-constructor) – jfs

0

की यूनिक्स व्यवहार के लिए कारण:

Read commands from the command_string operand instead of from the standard input. Special parameter 0 will be set from the command_name operand and the positional parameters ($1, $2, etc.) set from the remaining argument operands.

इस पैच काफी बस चाल करने के लिए लगता है shell=True उद्धरण के साथ करना है। जब हम एक शेल कमांड लिखते हैं, यह रिक्त स्थान पर विभाजित किया जाएगा, इसलिए हम कुछ तर्क उद्धृत करने के लिए है:

cp "My File" "New Location" 

इस समस्याओं को जन्म देता है जब हमारे तर्कों उद्धरण चिह्न है, जो भागने की आवश्यकता है:

grep -r "\"hello\"" . 

कभी-कभी हम awful situations प्राप्त कर सकते हैं जहां \ भी बच निकलना चाहिए!

बेशक

, असली समस्या यह है कि हम कई तार निर्दिष्ट करने के लिए एक स्ट्रिंग का उपयोग करने की कोशिश कर रहे हैं। जब प्रणाली आदेशों बुला, सबसे प्रोग्रामिंग भाषाओं, पहली जगह में कई तार भेजने के लिए हमें की अनुमति देकर इस से बचने इसलिए:

Popen(['cp', 'My File', 'New Location']) 
Popen(['grep', '-r', '"hello"']) 

कभी कभी यह "कच्चे" खोल आदेशों को चलाने के लिए अच्छा हो सकता है, उदाहरण के लिए, यदि हम एक शेल स्क्रिप्ट या वेबसाइट से कुछ कॉपी-पेस्ट कर रहे हैं, और हम मैन्युअल रूप से सभी भयानक भागने को परिवर्तित नहीं करना चाहते हैं। यही कारण है कि shell=True विकल्प उपलब्ध है:

Popen(['cp "My File" "New Location"'], shell=True) 
Popen(['grep -r "\"hello\"" .'], shell=True) 

मैं तो मैं नहीं जानता कि कैसे या क्यों इसे दूसरे तरीके से बर्ताव करता है विंडोज से परिचित नहीं हूँ।

संबंधित मुद्दे