问题
I've got a PowerShell script that needs to be able to call another PS script from within the same directory with elevated credentials. Because the scripts need to be portable, the file path of the scripts isn't known ahead of time and needs to be able to handle spaces and special characters in the file path without failing.
Since elevating a PS session will cause the current working directory to change to "C:\Windows\System32", I need to be able to pass the current directory to the script as a parameter. Now, my current script (below) is handling this fine as long as there aren't special characters in the directory path.
Here's my current script (updated per @mklement0's suggestion):
$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath
Start-Process Powershell -Verb runas -ArgumentList "-ExecutionPolicy","Bypass","-File","`"$dir\elevated.ps1`"","`"$dir`""
I've tried escaping some of the special characters, like so:
$dir = $dir.Replace('&','`"&`"')
but that's not working consistently for all the different characters that Windows allows in a folder/filename. Not all special characters need to be escaped either, but I need to make sure this works for the ones that do; at least those that are likely to show up in a filepath.
Special characters I'm mainly concerned with (though foreign characters might also need to be addressed at some point): !@#$%^_+-=.,{}`~&()[]
Example directory path I'm testing with:
C:\Users\Administrator\Desktop\Test Folder\badname-!@#$%^_+-=.,{}`~&()[]
Trying to run my script from the above location returns the following error:
The specified wildcard character pattern is not valid: badname-!@#$%^_+-=.,{}\~&()[] + CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : RuntimeException
So, what's the best way to have the script dynamically handle special characters in the directory path?
回答1:
As an aside: In PSv3+ you can use $PSScriptRoot
to refer to the directory in which the script is located.
If you can modify the target script, you can place Set-Location -LiteralPath $PSScriptRoot
at the top to change to that folder.
To avoid quoting/escaping problems, pass arguments individually, as distinct elements of an array to -ArgumentList
(-Args
):
Start-Process powershell -Verb runas -ArgumentList '-ExecutionPolicy', 'Bypass',
'-File', "`"$dir\elevated.ps1`"", "`"$dir`""
Unfortunately, that alone is not enough:
When you use
-File
, even though the arguments are passed individually, the script file path and the directory path still require embedded"..."
quoting in order to pass values with embedded spaces correctly:"`"..."`"
passes a value with embedded double-quoting (`
is PowerShell's escape character, so`"
escapes a double quote).Tip of the hat to Ansgar Wiechers.
This bug has been reported on GitHub.- In case you ever need to pass an argument with embedded
"
characters (which filesystem paths by definition never contain), use something like:('"{0}"' -f ($value -replace '"', '\"'))
- In case you ever need to pass an argument with embedded
Sadly, even that is not enough if your path contains
[
characters that aren't each part of a syntactically valid wildcard-pattern range (e.g.,[ab]
,[0-9]
), because PowerShell - as of Windows PowerShell v5.1 / PowerShell Core v6.0.0-beta.6 - mistakenly interprets the path as a wildcard pattern and fails if it finds it malformed - see this GitHub issue.- In other words: your invocation may or may not work, depending on how
[
and possibly]
are used in your path; e.g.foo[1]
works, butfoo[]
doesn't. - There is currently no workaround that works in all cases.
- In other words: your invocation may or may not work, depending on how
Alternative with -Command
:
-Commmand
allows you to pass a piece of PowerShell code - a mini-script, if you will - which you can use to pass a single, quoted string that is parsed by PowerShell rules by the new instance:
Start-Process powershell -Verb runas -ArgumentList '-ExecutionPolicy', 'Bypass',
'-Command', "& `"$dir\elevated.ps1`" `"$dir`""
Note the need to use &
to invoke the script, because it is given as a quoted string, and that the expansion of $dir
is performed by the current session.
Unfortunately, the problem with filenames that happen to look like malformed wildcard patterns (discussed above) applies to this invocation scenario too.
回答2:
So, I was able to figure out a solution that works as long as the brackets aren't empty []
.
Here's what I've got:
Start-Process Powershell -Verb runas -ArgumentList "-ExecutionPolicy","Bypass","-File","`"$(Get-Item -LiteralPath $PSScriptRoot\elevated.ps1)`""
Using this as the test path:
C:\Users\Administrator\Desktop\Test Folder\badname-!@#$%^_+-=.,{}`~[&]()
A path with empty brackets []
still doesn't work, but doesn't return an error. I'm assuming this means that it wasn't able to find a file in the expected path while interpreting the brackets as a wildcard still. Not sure if there's really anything that will fix that except maybe checking the path before running and throwing an error.
来源:https://stackoverflow.com/questions/45971875/how-to-handle-file-paths-with-special-characters