it-swarm.com.de

PowerShell entfernt doppelte Anführungszeichen von Befehlszeilenargumenten

In letzter Zeit hatte ich Probleme mit GnuWin32 von PowerShell, wenn doppelte Anführungszeichen verwendet werden.

Nach weiteren Untersuchungen scheint es so, als würde PowerShell doppelte Anführungszeichen aus Befehlszeilenargumenten streichen, selbst wenn diese ordnungsgemäß maskiert wurden.

PS C:\Documents and Settings\Nick> echo '"hello"'
"hello"
PS C:\Documents and Settings\Nick> echo.exe '"hello"'
hello
PS C:\Documents and Settings\Nick> echo.exe '\"hello\"'
"hello"

Beachten Sie, dass die doppelten Anführungszeichen an das echo cmdlet von PowerShell übergeben werden. Wenn sie jedoch als Argument an echo.exe übergeben werden, werden die doppelten Anführungszeichen entfernt, es sei denn, sie werden mit einem Backslash (nicht mit einem Escape-Zeichen versehen) versehen Der Escape-Charakter von PowerShell ist ein Backtick, kein Backslash.

Dies scheint mir ein Fehler zu sein. Wenn ich die korrekten Escape-Zeichenfolgen an PowerShell übergeben, sollte PowerShell sich darum kümmern, was für eine Escape-Aktion erforderlich ist, auch wenn der Befehl aufgerufen wird.

Was geht hier vor sich?

Zur Zeit besteht das Problem darin, Befehlszeilenargumente gemäß diesen Regeln zu umgehen (die anscheinend vom CreateProcess-API-Aufruf verwendet werden, den PowerShell zum Aufrufen von .exe-Dateien verwendet): 

  • Um ein doppeltes Anführungszeichen zu übergeben, aktivieren Sie das Kontrollkästchen mit einem umgekehrten Schrägstrich: \" -> "
  • Um einen oder mehrere Backslashes, gefolgt von einem doppelten Anführungszeichen, zu übergeben, müssen Sie jeden Backslash mit einem anderen Backslash und das Zitat entgehen lassen: \\\\\" -> \\"
  • Wenn nicht gefolgt von einem doppelten Anführungszeichen, ist für umgekehrte Schrägstriche kein Escaping erforderlich: \\ -> \\

Beachten Sie, dass es möglicherweise erforderlich sein muss, doppelte Anführungszeichen zu umgehen, um die doppelten Anführungszeichen in der Zeichenfolge der Windows-API-Escape-Zeichenfolge in PowerShell zu schützen.

Hier einige Beispiele mit echo.exe von GnuWin32:

PS C:\Documents and Settings\Nick> echo.exe "\`""
"
PS C:\Documents and Settings\Nick> echo.exe "\\\\\`""
\\"
PS C:\Documents and Settings\Nick> echo.exe "\\"
\\

Ich kann mir vorstellen, dass dies schnell zur Hölle werden kann, wenn Sie einen komplizierten Befehlszeilenparameter übergeben müssen. Natürlich ist nichts davon in der CreateProcess()- oder PowerShell-Dokumentation dokumentiert.

Beachten Sie auch, dass dies nicht erforderlich ist, um Argumente mit Anführungszeichen an .NET-Funktionen oder PowerShell-Cmdlets zu übergeben. Dafür müssen Sie nur Ihre doppelten Anführungszeichen an PowerShell übergeben.

36
nw.

Es ist eine bekannte Sache :

Es ist weit zu schwer, Parameter an Anwendungen zu übergeben, für die Strings erforderlich sind. Ich habe diese Frage in IRC mit einem "Raum" von PowerShell-Experten gestellt, und es hat Stunden gedauert, bis jemand einen Weg gefunden hat (ich habe ursprünglich hier angefangen zu posten, dass dies einfach nicht möglich ist). Dadurch wird die Fähigkeit von PowerShell, eine allgemeine Shell zu sein, vollständig unterbunden, da einfache Aufgaben wie das Ausführen von sqlcmd nicht ausgeführt werden können. Der Job Nummer eins einer Befehls-Shell sollte Befehlszeilenanwendungen ausführen. Wenn Sie beispielsweise versuchen, SqlCmd von SQL Server 2008 aus zu verwenden, gibt es einen Parameter -v, der eine Reihe von Parametern mit dem Namen: value verwendet. Wenn der Wert Leerzeichen enthält, müssen Sie ihn angeben ...

Es gibt keine einzige Möglichkeit, eine Befehlszeile zu schreiben, um diese Anwendung korrekt aufzurufen. Selbst wenn Sie alle 4 oder 5 verschiedenen Arten des Zitierens und Fluchtens von Dingen beherrschen, ahnen Sie immer noch, welche Funktion wann wirksam wird. oder, Sie können einfach auf cmd aussteigen und sind damit fertig.

29
manojlds

Ich persönlich vermeide es, '\' zu verwenden, um den Dingen in PowerShell zu entgehen, da es sich technisch nicht um einen Shell-Escape-Charakter handelt. Ich habe damit unvorhersehbare Ergebnisse erzielt. In Strings mit doppelten Anführungszeichen können Sie "" verwenden, um ein eingebettetes doppeltes Anführungszeichen zu erhalten, oder es mit einem Back-Tick zu maskieren:

PS C:\Users\Droj> "string ""with`" quotes"
string "with" quotes

Das gleiche gilt für einfache Anführungszeichen:

PS C:\Users\Droj> 'string ''with'' quotes'
string 'with' quotes

Das Seltsame beim Senden von Parametern an externe Programme ist, dass es eine zusätzliche Bewertungsebene gibt. Ich weiß nicht, ob dies ein Fehler ist, aber ich vermute, es wird nicht geändert, da das Verhalten dasselbe ist, wenn Sie Start-Process verwenden und Argumente übergeben. Start-Process nimmt ein Array für die Argumente, was die Dinge ein wenig klarer macht, in Bezug auf die Anzahl der tatsächlich gesendeten Argumente, aber diese Argumente scheinen eine zusätzliche Zeit zu sein.

Wenn ich also ein Array habe, kann ich die arg-Werte auf eingebettete Anführungszeichen setzen:

PS C:\cygwin\home\Droj> $aa = 'arg="foo"', 'arg=""""bar""""'
PS C:\cygwin\home\Droj> echo $aa
arg="foo"
arg=""""bar""""

Das Argument 'bar' reicht aus, um die zusätzliche versteckte Bewertung abzudecken. Es ist, als würde ich diesen Wert in Anführungszeichen an ein Cmdlet senden und dann das Ergebnis erneut in Anführungszeichen senden:

PS C:\cygwin\home\Droj> echo "arg=""""bar""""" # level one
arg=""bar""
PS C:\cygwin\home\Droj> echo "arg=""bar""" # hidden level 
arg="bar"

Man könnte erwarten, dass diese Argumente an externe Befehle übergeben werden, wie sie an Cmdlets wie 'echo'/'write-output' übergeben werden. Dies ist jedoch aufgrund der verborgenen Ebene nicht der Fall:

PS C:\cygwin\home\Droj> $aa = 'arg="foo"', 'arg=""""bar""""'
PS C:\cygwin\home\Droj> start c:\cygwin\bin\echo $aa -nonew -wait
arg=foo arg="bar"

Ich kenne den genauen Grund dafür nicht, aber das Verhalten ist, als ob ein weiterer, nicht dokumentierter Schritt unter den Covers gemacht würde, der die Zeichenketten neu analysiert. Zum Beispiel erhalte ich dasselbe Ergebnis, wenn ich das Array an ein Cmdlet sende. Fügen Sie jedoch eine Analyseebene hinzu, indem Sie es durch invoke-expression:

PS C:\cygwin\home\Droj> $aa = 'arg="foo"', 'arg=""""bar""""'
PS C:\cygwin\home\Droj> iex "echo $aa"
arg=foo
arg="bar"

... und genau das bekomme ich, wenn ich diese Argumente an mein externes cygwin 'echo.exe' sende:

PS C:\cygwin\home\Droj> c:\cygwin\bin\echo 'arg="foo"' 'arg=""""bar""""'
arg=foo arg="bar"
13
Droj

Sich auf die CMD zu verlassen, um das in der akzeptierten Antwort angegebene Problem zu lösen, funktionierte für mich nicht, da doppelte Anführungszeichen beim Aufruf der ausführbaren CMD-Datei immer noch entfernt wurden. 

Die beste Lösung für mich war, meine Befehlszeile als ein Array von Strings zu strukturieren, anstatt eines einzelnen vollständigen Strings, der alle Argumente enthält. Dann übergeben Sie dieses Array einfach als Argument für den binären Aufruf:

$args = New-Object System.Collections.ArrayList
$args.Add("-U") | Out-Null
$args.Add($cred.UserName) | Out-Null
$args.Add("-P") | Out-Null
$args.Add("""$($cred.Password)""")
$args.Add("-i") | Out-Null
$args.Add("""$SqlScriptPath""") | Out-Null
& SQLCMD $args

In diesem Fall werden doppelte Anführungszeichen für Argumente ordnungsgemäß an den aufgerufenen Befehl übergeben.

Bei Bedarf können Sie es mit EchoArgs in den PowerShell Community Extensions testen und debuggen.

1
GGirard

In aktuellen Versionen von PowerShell zum Zeitpunkt der Erstellung dieses Dokuments scheint dieses Problem behoben zu sein. Es gibt also keine Sorgen mehr.

Wenn Sie immer noch der Meinung sind, dass Sie dieses Problem sehen, denken Sie daran, dass es möglicherweise etwas anderes betrifft, beispielsweise das Programm, das PowerShell aufruft. Wenn Sie es also nicht direkt beim Aufrufen von PowerShell über eine Eingabeaufforderung oder die ISE reproduzieren können, sollten Sie an anderer Stelle debuggen.

Ich habe diese Frage zum Beispiel gefunden, als ich ein Problem des Verschwindens von Anführungszeichen untersuchte, wenn ein PowerShell-Scrip aus C # -Code mit Process.Start ausgeführt wird. Das Problem war eigentlich C # Prozessstart braucht Argumente mit doppelten Anführungszeichen - sie verschwinden

1
Florian Winter