PowerShell - 字符串中的复杂替换

PowerShell - Complex replacement in a string

关于文件名中的复杂替换,我有一个非常复杂的问题,我会尝试解释一下

我正在读取一个文件名,这个文件名在我们公司有约定的标志,例如:%20,_代表 不同的标志。

'%20' represent a space 
'_' represent '/'

根据这些迹象,我正在 TFS 中的正确位置搜索文件并且它工作正常 当 '' 真正代表 '/' 但当 '' 是名称的一部分时会发生什么?

让我们看一个简单的例子,文件名中只有一个'_' mail_sp.sql 它可以转换为:mail/sp.sql 或 mail_sp.sql 这是我写的代码(我不知道它是否有效,请随时纠正我)

CheckFileExistsTfs 是一个函数,return True 或 False 取决于文件是否存在于 TFS

function CheckFileExistsTfs {
    param(
        [Parameter(Mandatory)]
        [string] $tfsFilePath,
        [string] $userName,
        [string] $userPassword,
        [string] $userDomain
    )

    Add-Type -AssemblyName 'System.Net.Http'

    $clientHandler = New-Object System.Net.Http.HttpClientHandler

    if ($userName) {
        $clientHandler.Credentials = new-object System.Net.NetworkCredential($userName, $userPassword, $userDomain)
    }
    else {
        $clientHandler.UseDefaultCredentials = 1
    }
    
    $client = New-Object System.Net.Http.HttpClient($clientHandler)

    $tfsBranchPath = "https://tfs.bandit.com/DefaultCollection/Idu Client-Server/_apis/tfvc/items?scopePath=$tfsFilePath"

    #Write-Host "Sending get request (items api) to url: " $tfsBranchPath
    $response = $client.GetAsync($tfsBranchPath).Result
    $contentObjects = $response.Content.ReadAsStringAsync().Result | ConvertFrom-Json

    $exists = $contentObjects.count -eq 1
    
    $fileSize = 0
    
    if ($exists) {
        $fileSize = $contentObjects.value[0].size / 1024
    }   

    $result = [PSCustomObject]@{
        FileExists      = $exists
        FileSizeInBytes = $fileSize
    }

    return $result
}

$PatchBranch 是 TFS 中的路径,如 $/Idu Client-Server/Branches/V6.4/Releases/v8.6.24-CU/Database/

$sql = mail_sp.sql
$newname = $sql.Name.Replace('%20', ' ').replace('_', '/')
$filePathInTfs = $PatchBranch + $new
$fileExist = CheckFileExistsTfs $filePathInTfs

if ($fileExist)
{ Write-Host "TFS file existance is: [$fileExist] by path [$filePathInTfs]" }
                         
elseif (!$fileExist) {
    $new = $sql.Name.Replace('%20', ' ')
    $filePathInTfs = $PatchBranch + $new
    $fileExist = CheckFileExistsTfs $filePathInTfs

    if ($fileExist) 
    { Write-Host "TFS file existance is: [$fileExist] by path [$filePathInTfs]" }

}
else { Write-Host "TFS file not exist : [$fileExist] by path [$filePathInTfs] " }
                                                        

但是如果名字是mail_sp_sp.sql呢?甚至更多'_'将有很多组合:

mail/sp_sp.sql
mail/sp/sql.sql
mail_sp/sql.sql
mail_sp_sql.sql
mail/sp_sql.sql

附加示例:

AD%20mail_sp.sql 应转换为:

  AD mail_sp.sql
  AD mail/sp.sql

目标是检查每个组合,如果其中一个为真,则停止迭代并显示一条消息,只有当所有尝试都失败时,然后 return 为假或给出正确的消息。

我要问的是检查第一个代码是否有效(当只有一个'_'时)并帮助检查复杂的组合 谢谢

序言

根据评论,解释你的问题以确保我理解它,你有一个文件路径,你通过用 _ 替换 / 来编码(space) 与 %20。您想反转编码以返回原始文件路径,但您的编码不明确:

    编码字符串中的
  • _ 可能意味着原始文件路径中的 /_
  • 编码字符串中的
  • %20 可能意味着原始文件路径中的 %20 (space)

因此,您需要生成所有可能的原始值,然后找到文件系统中存在的值。

顺便说一句,这就是为什么很多编码都有“转义序列”的原因,这样您就可以知道字符串的某些部分是文字还是标记 - 例如在 C# var x = "line1\nline2\nline3"; 中 - \n 明确表示换行符。如果你想要字符串文字 \n 而不是你必须像这样转义 \var x = "line1\nline2\nline3";。这样,任何编码字符串只能表示一个原始值。

如果可以,您可能希望通过添加转义字符来重新访问编码规则,以使编码的字符串明确无误。

回答

我不久前写了一个关于将带有西里尔字符的单词音译成您可以重新利用的拉丁字符的不同问题的答案 - 请参阅

您需要做的就是像这样更改查找 table:

function ConvertTo-DecodedPath
{
    param(
        [string] $InputString
    )

    $lookups = [ordered] @{
        # single character substitutions
        # (we need to use the [char] cast to force case sensitivity for keys)
        [char] "_" = @( "_", "/" )
        # multi-character substitutions
        [string] "%20" = @( "%20", " " )
    }

    # if the input is empty then there's no work to do,
    # so just return an empty string
    if( [string]::IsNullOrEmpty($InputString) )
    {
        return [string]::Empty;
    }

    # find all the lookups that can be applied at the start of the string
    $keys = @( $lookups.Keys | where-object { $InputString.StartsWith($_) } );

    # if there are no lookups found at the start of the string we'll keep
    # the first character as-is and prefix it to all the transliterations
    # for the remainder of the string
    if( $keys.Length -eq 0 )
    {
        $results = @();
        $head    = $InputString[0];
        $rest    = $InputString.Substring(1);
        $tails   = ConvertTo-DecodedPath -InputString $rest;
        foreach( $tail in $tails )
        {
            $results += $head + $tail;
        }
        return $results;
    }

    # if we found any lookups at the start of the string we need to "multiply"
    # them with all the transliterations for the remainder of the string
    $results = @();
    foreach( $key in $keys )
    {
        if( $InputString.StartsWith($key) )
        {
            $heads = $lookups[$key];
            $rest  = $InputString.Substring(([string] $key).Length);
            $tails = ConvertTo-DecodedPath -InputString $rest;
            foreach( $head in $heads )
            {
                foreach( $tail in $tails )
                {
                    $results += $head + $tail;
                }
            }

        }
    }

    return $results;

}

例子

这是一个基于您的示例字符串的示例。请注意,这些值与您列出的值不同,但我认为您的列表可能是错误的,因为我看不出 mail_sp_sp.sql 如何成为您给出的以 [=28= 结尾的任何字符串] :-).

PS> ConvertTo-DecodedPath "mail_sp_sp.sql"
mail_sp_sp.sql
mail_sp/sp.sql
mail/sp_sp.sql
mail/sp/sp.sql

问题

如果存在多个解码路径,接下来会发生什么?