From b0c4969472a8a101a40631d124047dfb8df9c587 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Wed, 7 Jun 2017 13:37:43 -0700 Subject: [PATCH] when powershell is used in a shebang script (#!/usr/bin/powershell) without a ps1 extension, powershell treats it as a native command so this ends up in a recursive loop. fix is to inspect the command to see if it is a shebang script and one we should handle. if so, just treat it like a ps1 script. --- .../engine/CommandSearcher.cs | 36 ++++++++++++++++++- test/powershell/Host/ConsoleHost.Tests.ps1 | 12 +++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/CommandSearcher.cs b/src/System.Management.Automation/engine/CommandSearcher.cs index 65ac77eb577..142352aa98b 100644 --- a/src/System.Management.Automation/engine/CommandSearcher.cs +++ b/src/System.Management.Automation/engine/CommandSearcher.cs @@ -635,7 +635,41 @@ private CommandInfo GetInfoFromPath(string path) break; } - if (String.Equals(extension, StringLiterals.PowerShellScriptFileExtension, StringComparison.OrdinalIgnoreCase)) + // handle case where we are called by the OS to handle a shebang script using us as the interpreter + bool isShebangScript = false; + try + { + using(FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read)) + { + byte[] bytes = new byte[2]; + int bytesRead = fileStream.Read(bytes, 0, 2); // read the first two bytes to determine if shebang + if (bytesRead == 2) + { + if (bytes[0] == '#' && bytes[1] == '!') + { + // see if we are supposed to be the interpreter + using(StreamReader file = new StreamReader(fileStream)) + { + string interpreter = file.ReadLine(); + System.Reflection.Assembly assembly = System.Reflection.Assembly.GetEntryAssembly(); + // this returns path to powershell.dll + string powershellPath = assembly.Location.Replace(".dll",""); + // need to handle both powershell and powershell.exe + if (interpreter.Split(' ',2)[0].Replace(".exe","") == powershellPath) + { + isShebangScript = true; + } + } + } + } + } + } + catch(Exception) + { + // If we can't read the file, safe to assume it's not a script + } + + if (isShebangScript || String.Equals(extension, StringLiterals.PowerShellScriptFileExtension, StringComparison.OrdinalIgnoreCase) || isShebangScript) { if ((_commandTypes & CommandTypes.ExternalScript) != 0) { diff --git a/test/powershell/Host/ConsoleHost.Tests.ps1 b/test/powershell/Host/ConsoleHost.Tests.ps1 index 50dd8fb17df..bd0ac3a1960 100644 --- a/test/powershell/Host/ConsoleHost.Tests.ps1 +++ b/test/powershell/Host/ConsoleHost.Tests.ps1 @@ -176,6 +176,18 @@ Describe "ConsoleHost unit tests" -tags "Feature" { # no extraneous output $observed | should be $currentVersion } + + $testCases = + @{interpreter = "#!${pshome}/powershell"}, + @{interpreter = "#!${pshome}/powershell.exe"} + + It "Should properly execute shebang script that doesn't have .ps1 extension" -TestCases $testCases { + param($Interpreter) + $scriptPath = "~/shebangtest" + Set-Content -Path $scriptPath -Value $Interpreter + Add-Content -Path $scriptPath -Value "'hello'" + & $powershell $scriptPath | Should BeExactly 'hello' + } } Context "Pipe to/from powershell" {