ichisich hat geschrieben:Schön wäre eine Erklärung warum !?
Gut, ich wollt das nicht extra schreiben weil das recht lang ist zum erklären. Aber dann mach ich das einmal, dann kann ich drauf linken.
Also, Prozesse werden unter Unix gestartet indem erstmal
fork() aufgerufen wird, was den aktuellen Prozess *kopiert* und dann im Kindprozess
exec() aufgerufen wird, dass den Prozess durch einen neuen ersetzt. Wenn man sich die Parameter der ``exec*()``-Funktionen anschaut, insbesondere ``arg0`` dann sieht man dort, dass das ein Character-Array ist. Also werden die Aufrufparameter an den Kernel nicht als String mit Quotes und sowas übergeben sondern ein Array von Strings. In jedem String ist genau *ein* Parameter. Wer schon mal in C programmiert hat wird da den ``argv``-Parameter der ``main``-Funktion wiedererkennen, denn genau dort werden vom Kernel die Parameter schließlich weitergeleitet.
Gut, also um Prozesse aufzurufen, wissen wir nun, werden Arrays übergeben. Eine naive Shell würde dem User genau diese Möglichkeit geben, ein Aufruf von ``wget`` könnte etwa so aussehen:
Code: Alles auswählen
$ ["wget", "-U", "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.12) Gecko/20101117 Firefox/3.6.12", "http://www.python-forum.de/"]
Das wäre der direkteste Weg, aber man stellt schnell fest, dass das sehr umständlich ist, gerade dass man jeden Parameter extra quoten muss. Man könnte doch eine Shell erfinden, die eine etwas einfache Syntax besitzt bei der einfach die Einträge in dem Array durch Leerzeichen getrennt werden, statt mit Kommas und Doublequotes. Die eckigen Klammern kann man sich ja auch sparen, also rufen wir mal auf:
Code: Alles auswählen
$ wget -U Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.12) Gecko/20101117 Firefox/3.6.12 http://www.python-forum.de/
Was die Shell dann zu
Code: Alles auswählen
$ ["wget", "-U", "Mozilla/5.0", "(X11;", "U;", "Linux", "x86_64;", "en-US;", "rv:1.9.2.12)", "Gecko/20101117", "Firefox/3.6.12", "http://www.python-forum.de/"]
expandieren würde. Uh-oh! Da stimmt was nicht, plötzlich bekommt ``wget`` ja viel zu viele Argumente! Also müssen wir der Shell beibringen dass der eine String eben nicht an den Leerzeichen getrennt werden soll. Dazu könnten wir den String in Quotes stellen:
Code: Alles auswählen
$ wget -U "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.12) Gecko/20101117 Firefox/3.6.12" http://www.python-forum.de/
würde dann von der Shell geparst werden als:
Code: Alles auswählen
$ ["wget", "-U", "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.12) Gecko/20101117 Firefox/3.6.12", "http://www.python-forum.de/"]
Das sieht schon ziemlich gut aus. Man sieht auch, dass die Programme die aufgerufen werden, also etwa ``wget`` den Useragent ohne Quotes übermittelt bekommen, denn die Quotes sind nur für die Shell gedacht.
Wenn wir nun Subprocess nutzen haben wir die Möglichkeit einen String anzugeben, der dann von einer Shell ausgeführt wird. Dazu müssen wir den String zusammenbauen und Quoten und so und die Shell aufrufen. Die Shell macht dann nichts anderes als das ganze Quoting wieder rückgängig zu machen und das Programm mit einem Array aufzurufen. Daher kann man in ``Popen`` auch *direkt* eine Liste mit Parametern angeben, so dass da *nichts* gequotet werden muss, da man ``argv``/``arg0`` direkt so übergibt.
Was ich auch schon gesehen habe als Fehler war, dass die Leute die Schalter sowie deren Werte als *einen* Parameter übergeben:
Code: Alles auswählen
$ wget "-U Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.12) Gecko/20101117 Firefox/3.6.12" http://www.python-forum.de/
dieser Aufruf ist falsch, da das -U und der Useragent zwei separate Argumente sind. Wenn man das so macht wird es zu:
Code: Alles auswählen
$ ["wget", "-U Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.12) Gecko/20101117 Firefox/3.6.12", "http://www.python-forum.de/"]
ausgewertet, aber ``wget`` kennt so einen Schalter in dem Spaces vorkommen nicht.
Etwas anderes ist, wenn man die lange Form verwendet:
Code: Alles auswählen
$ wget "--user-agent=Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.12) Gecko/20101117 Firefox/3.6.12" http://www.python-forum.de/
wird zu
Code: Alles auswählen
$ ["wget", "--user-agent=Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.12) Gecko/20101117 Firefox/3.6.12", "http://www.python-forum.de/"]
und damit kann ``wget`` umgehen, weil erwartet wird dass ``--user-agent=`` und der Wert als *ein* Parameter übergeben wird.
Deswegen ist ichistichs Ansatz des Debuggens über die die Stringausgabe Quatsch, da man für die String-Variante ja extra quoten *muss* (damit die Shell weiß was sie in das Array packen muss), was man bei der Array-Variante gar *nicht darf*. Also macht man sich damit das Leben nur noch schwerer. Sinnvoller wäre es, zu überlegen wie der Shell-Aufruf in "Array-Syntax" aussehen würde, wie ich hier in dem Post vorgemacht habe.