{"id":487,"date":"2021-05-22T10:00:00","date_gmt":"2021-05-22T08:00:00","guid":{"rendered":"https:\/\/pascal-korz.de\/blog\/?p=487"},"modified":"2021-04-21T23:07:54","modified_gmt":"2021-04-21T21:07:54","slug":"powershell-fallen-5-doppelte-einfache-maskierte-anfuehrungszeichen","status":"publish","type":"post","link":"https:\/\/pascal-korz.de\/blog\/2021\/05\/22\/powershell-fallen-5-doppelte-einfache-maskierte-anfuehrungszeichen\/","title":{"rendered":"PowerShell-Fallen #5: Doppelte, einfache, maskierte Anf\u00fchrungszeichen"},"content":{"rendered":"\n<p>Gerade f\u00fcr Pfadangaben sind &#8222;G\u00e4nsef\u00fc\u00dfchen&#8220; unverzichtbar, damit Whitespace als Teil des Pfades interpretiert wird. Klassiker: <code>C:\\Program Files<\/code><\/p>\n\n\n\n<p>Manchmal soll der Pfad aber gar nicht sofort von PowerShell verwertet, sondern zusammen mit den Anf\u00fchrungszeichen irgendwohin geschrieben werden, um sie dann sp\u00e4ter von einem anderen Prozess wieder einzulesen. So m\u00f6chte man zum Beispiel eine geplante Augabe in Windows mit PowerShell anlegen und den kompletten Befehl mit allen Argumenten samt Dateipfaden darin speichern. Doch wie macht man das?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wie es nicht geht<\/h2>\n\n\n\n<p>Offensichtlich kann man es nicht so schreiben:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$action = \"PowerShell.exe -File \"D:\\My Scripts\\Download-Files.ps1\"\"\n\nOutput:\nUnexpected token 'D:\\My' in expression or statement.<\/code><\/pre>\n\n\n\n<p>Was ist passiert? Das erste Paar von Anf\u00fchrungszeichen kennzeichnet <code>PowerShell.exe -File <\/code> als String, und die Zeichen danach werden erfolglos als Befehl interpretiert.<\/p>\n\n\n\n<p>Die folgende Variante produziert zwar keinen Fehler, ist aber trotzdem falsch, weil die Anf\u00fchrungszeichen um den Pfad in der Ausgabe fehlen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$file_path = \"D:\\My Scripts\\Download-Files.ps1\"\n$action = \"PowerShell.exe -File $file_path\"\n$action\n\nOutput:\nPowerShell.exe -File D:\\My Scripts\\Download-Files.ps1<\/code><\/pre>\n\n\n\n<p>So ausgef\u00fchrt w\u00fcrde der Befehl fehlschlagen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Die zweitbeste L\u00f6sung<\/h2>\n\n\n\n<p>Erfolg verspricht auf den ersten Blick, den Pfad in doppelten Anf\u00fchrungszeichen zu belassen, aber daf\u00fcr den String in einfache Anf\u00fchrungszeichen zu fassen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$action = 'PowerShell.exe -File \"D:\\My Scripts\\Download-Files.ps1\"'\n$action\n\nOutput:\nPowerShell.exe -File \"D:\\My Scripts\\Download-Files.ps1\"<\/code><\/pre>\n\n\n\n<p>Es funktioniert auch umgekehrt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$action = \"PowerShell.exe -File 'D:\\My Scripts\\Download-Files.ps1'\"\n$action\n\nOutput:\nPowerShell.exe -File 'D:\\My Scripts\\Download-Files.ps1'<\/code><\/pre>\n\n\n\n<p>Man macht sich dabei zunutze, dass Zeichen zwischen gleichartigen Anf\u00fchrungszeichen als Strings interpretiert werden.<\/p>\n\n\n\n<p>Was w\u00e4re, wenn der Befehl Variablen ben\u00f6tigte?<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$folder = \"folder_a\"\n$timeout = 3600\n$action_1 = 'PowerShell.exe -File \"D:\\My Scripts\\$folder\\Download-Files.ps1\" -Timeout $timeout'\n$action_1\n$action_2 = \"PowerShell.exe -File 'D:\\My Scripts\\$folder\\Download-Files.ps1' -Timeout $timeout\"\n$action_2\n\nOutput:\nPowerShell.exe -File \"D:\\My Scripts\\$folder\\Download-Files.ps1\" -Timeout $timeout\nPowerShell.exe -File 'D:\\My Scripts\\folder_a\\Download-Files.ps1' -Timeout 3600<\/code><\/pre>\n\n\n\n<p>Bei <code>$action_1<\/code> wurden wegen der einfachen Anf\u00fchrungszeichen keine Variablen aufgel\u00f6st. Wenn der String wie bei <code>$action_2<\/code> von doppelten Anf\u00fchrungszeichen eingerahmt wird, l\u00f6st PowerShell die Variablen jedoch auf \u2013 ist das also unsere L\u00f6sung?<\/p>\n\n\n\n<p>Nun, wahrscheinlich in den meisten F\u00e4llen. In unserem Beispiel ist der Befehl hingegen f\u00fcr eine Aktion einer geplanten Aufgabe bestimmt \u2013 und dort wird CMD-Syntax erwartet. Das bedeutet: Einfache Anf\u00fchrungszeichen sind nicht erlaubt. Der Dateipfad im Skript muss von doppelten Anf\u00fchrungszeichen umrahmt sein, aber das geht ja nicht, wenn schon der String doppelte Anf\u00fchrungszeichen nutzt \u2026<\/p>\n\n\n\n<p>Jetzt haben wir schon so viel Zeit investiert und immer noch keine L\u00f6sung!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Sicher mit Maske auch in PowerShell<\/h2>\n\n\n\n<p>Gl\u00fccklicherweise wird alles ganz einfach, wenn man wei\u00df, was Maskierungszeichen oder <em>escape characters<\/em> sind. Kurz gesagt erf\u00fcllen die Anf\u00fchrungszeichen eine bestimmte Funktion f\u00fcr die Programmiersprache, aber wenn man ihnen das Maskierungszeichen voranstellt, verlieren sie diese Funktion und werden stattdessen als Text interpretiert. In PowerShell ist das Maskierungszeichen das Gravis-Akzentzeichen <code>`<\/code> und f\u00fchrt zum gew\u00fcnschten Ergebnis, wenn man die Anf\u00fchrungszeichen des Dateipfads damit schm\u00fcckt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$folder = \"folder_a\"\n$timeout = 3600\n$action = \"PowerShell.exe -File `\"D:\\My Scripts\\$folder\\Download-Files.ps1`\" -Timeout $timeout\"\n$action\n\nOutput:\nPowerShell.exe -File \"D:\\My Scripts\\folder_a\\Download-Files.ps1\" -Timeout 3600<\/code><\/pre>\n\n\n\n<p>Die doppelten Anf\u00fchrungszeichen bleiben erhalten, und die Variablen werden korrekt eingesetzt. So kann der Befehl zum Beispiel in der CMD oder in einer geplanten Aufgabe eingesetzt werden.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Gerade f\u00fcr Pfadangaben sind &#8222;G\u00e4nsef\u00fc\u00dfchen&#8220; unverzichtbar, damit Whitespace als Teil des Pfades interpretiert wird. Klassiker: C:\\Program Files Manchmal soll der Pfad aber gar nicht sofort von PowerShell verwertet, sondern zusammen mit den Anf\u00fchrungszeichen irgendwohin geschrieben werden, um sie dann sp\u00e4ter von einem anderen Prozess wieder einzulesen. So m\u00f6chte man zum Beispiel eine geplante Augabe in&hellip; <a class=\"more-link\" href=\"https:\/\/pascal-korz.de\/blog\/2021\/05\/22\/powershell-fallen-5-doppelte-einfache-maskierte-anfuehrungszeichen\/\"><span class=\"screen-reader-text\">PowerShell-Fallen #5: Doppelte, einfache, maskierte Anf\u00fchrungszeichen<\/span> weiterlesen<\/a><\/p>\n","protected":false},"author":6,"featured_media":429,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-487","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-powershell","entry"],"_links":{"self":[{"href":"https:\/\/pascal-korz.de\/blog\/wp-json\/wp\/v2\/posts\/487","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pascal-korz.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pascal-korz.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pascal-korz.de\/blog\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/pascal-korz.de\/blog\/wp-json\/wp\/v2\/comments?post=487"}],"version-history":[{"count":1,"href":"https:\/\/pascal-korz.de\/blog\/wp-json\/wp\/v2\/posts\/487\/revisions"}],"predecessor-version":[{"id":488,"href":"https:\/\/pascal-korz.de\/blog\/wp-json\/wp\/v2\/posts\/487\/revisions\/488"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/pascal-korz.de\/blog\/wp-json\/wp\/v2\/media\/429"}],"wp:attachment":[{"href":"https:\/\/pascal-korz.de\/blog\/wp-json\/wp\/v2\/media?parent=487"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pascal-korz.de\/blog\/wp-json\/wp\/v2\/categories?post=487"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pascal-korz.de\/blog\/wp-json\/wp\/v2\/tags?post=487"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}