Powershell: Apostrophe in filepath breaks Import-Module when the module has a class in it

Created on 6 Jul 2017  ·  8Comments  ·  Source: PowerShell/PowerShell

When there is an apostrophe in the filepath to a module Import-Module will work with modules that only have functions in however will fail if there is a class in the module. The exact same code will work when the module is located on a different path.

This situation was encountered due to the default OneDrive for Business path including an apostrophe. When using the user windowspowershell module with folder redirection all use modules now have an apostrophe in the path.

This affects both Version 5.1 and Version 6beta3

Steps to reproduce

Create Folder C:\Te'mp
Save the below code to C:\Te'mp\TypeName.psm1

# Define a class
class TypeName
{
   # Property with validate set
   [ValidateSet("val1", "Val2")]
   [string] $P1

   # Constructor
   TypeName ([string] $s)
   {
       $this.P1 = $s       
   }
}

Import-Module 'C:\te''mp\TypeName.psm1'

Expected behavior

Module Loaded Successfully / No Output

Actual behavior

Import-Module : The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)
At line:1 char:1
+ Import-Module 'C:\Te''mp\TypeName.psm1'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Import-Module], FileLoadException
    + FullyQualifiedErrorId : System.IO.FileLoadException,Microsoft.PowerShell.Commands.ImportModuleCommand

Environment data

> $PSVersionTable
Name                           Value
----                           -----
PSVersion                      6.0.0-beta
PSEdition                      Core
GitCommitId                    v6.0.0-beta.3
OS                             Microsoft Windows 10.0.14393
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Name                           Value                                                                                                        
----                           -----                                                                                                        
PSVersion                      5.1.15063.413                                                                                                
PSEdition                      Desktop                                                                                                      
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}                                                                                      
BuildVersion                   10.0.15063.413                                                                                               
CLRVersion                     4.0.30319.42000                                                                                              
WSManStackVersion              3.0                                                                                                          
PSRemotingProtocolVersion      2.3                                                                                                          
SerializationVersion           1.1.0.1
Issue-Bug Resolution-Fixed WG-Engine

All 8 comments

@ld0614 Thanks for your report!
Related PR #4136 - it seems we need full review allowed chars for "file names" vs "assembly names".

I've written a (very quick and VERY dirty) script which loops through all visible ASCII chars and outputs the ones that fail. In version 5.1 the following fail: 39 ('), 44 (,) and 61 (=). Interestingly enough testing with version 6.0 Beta 3 the same 3 fail as well as 126 (~)

$InvalidDirectoryChars = 34,42,47,58,60,62,63,92,124
$FailedChars = @()

for ($i = 32; $i -lt 255; $i++)
{
    $PathName = "C:\Temp\$($i)Te$([Char]$i)mp"
    if ($InvalidDirectoryChars -notcontains $i)
    {
        $Directory = New-Item -ItemType Directory -Path $PathName -Force -ErrorAction SilentlyContinue
        if ($Directory -eq $null)
        {
            Write-Output "Char $([Char]$i) is not a valid folder name number is: $i"
        }
        else
        {
            Copy-Item C:\Temp\Sample\* $PathName -Force
        }
        if ($i -ne 91)
        {
            Import-Module $PathName\FunctionModule.psm1 -ErrorAction SilentlyContinue -Force
        }
        else
        {
            Import-Module 'C:\Temp\91Te`[mp\FunctionModule.psm1' -Force
        }
        $FunctionModule = Get-Module FunctionModule
        if ($FunctionModule.Path -eq "$PathName\FunctionModule.psm1")
        {
            Write-Host "Char $i Loaded FunctionModule Correctly" -ForegroundColor Green
        }
        else
        {
            Write-Host "Char $i failed to load FunctionModule" -ForegroundColor Red
        }

        if ($i -ne 91)
        {
            Import-Module $PathName\ClassModule.psm1 -ErrorAction SilentlyContinue
        }
        else
        {
            Import-Module 'C:\Temp\91Te`[mp\ClassModule.psm1' -Force
        }
        $ClassModule = Get-Module ClassModule
        if ($ClassModule.Path -eq "$PathName\ClassModule.psm1")
        {
            Write-Host "Char $i Loaded ClassModule Correctly" -ForegroundColor Green
        }
        else
        {
            Write-Host "Char $i failed to load ClassModule" -ForegroundColor Red
            $FailedChars += $i
        }
    }
}

Write-Output "Failed Chars are:"
$FailedChars

This can vary on different platforms. We need more predictable way. I tried to find root code in CoreCLR repo. It seems the exception is here and failed code is here but I was not able to take the next step 😕

Whether or not this eventually gets fixed at the .Net level, I presume we want to add the work arounds in the PowerShell code in the meantime.

I can add three more Replace methods to the one that was merged last week for the comma.

We can use ’, [char]0x2019, the right quotation mark, to replace the apostrophe.
We can use ∼, [char]0x223C, the tilde operator, to replace the tilde.

The equals sign is not as straightforward.
=, [char]0xFF1D, the full width equals sign, as the only character I can find that looks like an equals sign is the most likely choice. The downside is that it is very wide, with whitespace built in, making it wider than standard ASCII characters, which throws off the spacing in plain text, which is probably no big deal but feels weird.
≃, [char]0x2248; ≐, [char]0x2250; and ≕, [char]0x2255 are close, but not quite matches for an equals sign. They, too, are wider than standard ASCII characters by varying amounts, but not as wide as the full width equals sign.
≈, [char]0x2243, is the right width, but it looks the least like an equals sign of the alternatives, and mathematically has a different meaning than an equals sign (almost or approximately equals). (The above three alternatives also have different meanings, but are less common so fewer people would be bothered by their mathematical differences.)

And don't forget that you can now use the Unicode esc sequence in dq strings e.g. "`u{2019}". :-)

This can also be reproduced in a simpler set up:
Save the TypeName.psm1 file as TypeName.ps1 and execute that.
You will get an error if e.g. the path contains a comma.

@donlassini - this bug was fixed in 71d5439bbe02e8db3df4ea93eda96665c293463b - are you still seeing the problem in the latest build?

I can confirm that we can import module with apostrophe in the file path in latest builds.

Was this page helpful?
0 / 5 - 0 ratings