Powershell:我需要帮助来修改冗长的 SQL 脚本文件

Powershell: I need help to modify a lengthy SQL script file

我有一个冗长的 SQL 脚本,其中包含 MANY/multiple 个像这样的部分(以及其他脚本部分):

USE [NAVDB]
GO

IF NOT EXISTS (SELECT name FROM sys.database_principals 
               WHERE name = '@@Placeholder@@')  
    CREATE USER [MyDomain\adcsuser] 
    FOR LOGIN [MyDomain\adcsuser] 
    WITH DEFAULT_SCHEMA = [MyDomain\adcsuser]
GO

GRANT CONNECT TO [MyDomain\adcsuser] AS [dbo]
GO

我需要解析此脚本块并仅修改脚本的 IF NOT EXISTS...CREATE USER... 行,以便立即将“@@Placeholder@@”替换为同一行中方括号 [] 内的文本在 CREATE USER 字符串之后。

因此,上面代码段中的行将变为:

IF NOT EXISTS (SELECT name FROM sys.database_principals 
               WHERE name = 'MyDomain\adcsuser')  
    CREATE USER [MyDomain\adcsuser] 
    FOR LOGIN [MyDomain\adcsuser] 
    WITH DEFAULT_SCHEMA = [MyDomain\adcsuser]

该文件有数百行,其中包含许多(至少)这些部分。

NotePad++ 查找替换和宏无法处理它,因为 []s 之间的名称中有“\”,我找不到如何让 NP++ 复制 []s 之间的文本。

此外,我尝试查看其他相关答案,例如:How can I replace every occurrence of a String in a file with PowerShell?,但到目前为止仍然受阻。

由于脚本文件结构的复杂性,我对 "read the whole file and just use Regex find/replace" 方法持怀疑态度,所以我一直在寻找一种更... RBAR(按痛苦行排列)方法。

动态 SQL 或参数化方法和类似建议不适用于这种情况,因为脚本已经从现有生产系统动态生成(而且我没有生成此脚本的实用程序的源代码作为输出)。我真的不能像那样进行大规模的结构更改。

此外,在再次阅读此 post 之后,我应该指出整个 "IF NOT EXISTS...WITH DEFAULT_SCHEMA [*]" 命令在脚本文件中位于一行中(如果不够清楚的话)。

对于 NotePad++,查找和替换支持正则表达式。

如何查找所有包含 "CREATE USER [someusername]" 的行并将其替换为您的替换 @@Placeholder@@ 的示例是:

.* 是通配符,方括号是正则表达式中的特殊字符,因此要将它们作为搜索的一部分包含在内,您必须将它们转义。所以 \[.*\] 会找到括号中的任何内容。我刚刚添加了 CREATE USER 作为查找所有这些特定行的示例。

确保在搜索模式中 select "Regular expression"。

PowerShell,所有内容都在一行中,您可以读取每一行,找到匹配项,提取用户,然后替换;将该行复制回新文件。

输入测试文件:

示例 PowerShell 脚本:

#create new output file
new-item "C:\temp\test2.txt" -Force
foreach ($line in (get-content "C:\temp\test.txt"))
{
    #Find the the user between the brackets and remove the brackets.
    $user = ([regex]"\[.*\]").Match($line).value.replace("[","").replace("]","")
    #Replace PlaceHolder with user pulled from the brackets and write to new file
    $line -replace '@@PlaceHolder@@', $user | out-file "C:\temp\test2.txt" -Append  
}

则输出文件内容:

@Tim Mylott,

整个问题是由我的生产系统通过 Powershell 的 DBATools 的 Export-DbaLogin 命令生成的脚本引起的。

该工具的输出脚本就像您的第一个 text.txt 文件一样,它会发出盲目的 CREATE USER 命令,而不会首先测试用户是否已经存在。

这不足以满足我们的需求(我们的生产数据库和系统已经存在 ,现在到处都是旧的、坏的 'lint') ,所以我将脚本输出放入 Notepad++ 并添加 IF NOT EXIST... 测试逻辑(有关详细信息,请参阅原始问题中的脚本)作为脚本中 CREATE USER 命令的前缀,并进行全局搜索和替换。只是,我不得不在 SELECT 的 WHERE 子句中为用户名输入 @@Placeholder@@ 以进行测试。

这给我留下了一个脚本,我必须在脚本文件的同一行中用现有 CREATE USER 文本中的实际用户名字符串替换 @@Placeholder@@ 文本。

在您引导我找到的 NP++ 中,解决方案是在 NP++ 搜索中使用正则表达式 select 同一行中的用户 ID 字符串。从那里开始,使用 NP++ 宏记录来自动搜索和替换是相当容易和直接的。

首先,我在网上找到了 select 第一对匹配 [] 之间的文本的正则表达式,即:(?<=[).+?(?=]) NP++脚本录制基本是这样(开始录制宏):

查找:@@PlaceHolder@@(非正则表达式搜索)[NP++ 查找下一行进行修改] [Home](找到行首) 查找:(?<=[).+?(?=])(正则表达式搜索)[NP++ selects 行的 CREATE USER 部分中的 UserID 字符串) [Ctrl+C](关闭查找对话框后将 UserID 字符串复制到剪贴板) [Home](再次找到行首) 查找:@@PlaceHolder@@(非正则表达式搜索)[NP++ 在当前行中查找 @@Placeholder@@ 文本并 selects 它] [Ctrl+V](粘贴剪贴板中的 UserID 字符串) (这会将光标留在同一位置,可以通过视觉验证更正后的命令是否正确)

在 NP++ 中,使用“播放宏”按钮手动重播这个录制的宏几次,以确保宏正确... 然后,多次使用播放(直到到达文件末尾),瞧! 脚本文件全部搞定

[注意:DBATools GitHub 现在有 2 个错误 reports/requests 将这个 IF NOT EXISTS 逻辑添加到生成的脚本中的 CREATE USER 行,所以这个问题最终会消失,但是现在...至少有一个使用 NotePad++ 的合理解决方案。]

所以,谢谢你,蒂姆,让我快速回答。

不过,您的 Powershell 部分答案是错误的,所以没有分数! (这是错误的做法,将“@@Placeholder@@”放入脚本中,而不是用脚本中已有的实际用户 ID 字符串替换脚本中现有的“@@PlaceHolder@@”字符串。

重复一遍,问题(逻辑上)是:

-- FROM THIS:
IF NOT EXISTS (SELECT ... WHERE name = '@@PlaceHolder@@')  CREATE USER [MyDomain\AUser2]...
IF NOT EXISTS (SELECT ... WHERE name = '@@PlaceHolder@@')  CREATE USER [MyDomain\AUser3]...
IF NOT EXISTS (SELECT ... WHERE name = '@@PlaceHolder@@')  CREATE USER [MyDomain\AUser5]...
IF NOT EXISTS (SELECT ... WHERE name = '@@PlaceHolder@@')  CREATE USER [MyDomain\AUser1]...
IF NOT EXISTS (SELECT ... WHERE name = '@@PlaceHolder@@')  CREATE USER [MyDomain\AUser7]...
IF NOT EXISTS (SELECT ... WHERE name = '@@PlaceHolder@@')  CREATE USER [MyDomain\AUser8]...
IF NOT EXISTS (SELECT ... WHERE name = '@@PlaceHolder@@')  CREATE USER [MyDomain\AUserA]...
IF NOT EXISTS (SELECT ... WHERE name = '@@PlaceHolder@@')  CREATE USER [MyDomain\AUserB]...
IF NOT EXISTS (SELECT ... WHERE name = '@@PlaceHolder@@')  CREATE USER [MyDomain\AUserC]...


-- TO THIS:
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUser2')  CREATE USER [MyDomain\AUser2]...
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUser3')  CREATE USER [MyDomain\AUser3]...
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUser5')  CREATE USER [MyDomain\AUser5]...
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUser1')  CREATE USER [MyDomain\AUser1]...
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUser7')  CREATE USER [MyDomain\AUser7]...
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUser8')  CREATE USER [MyDomain\AUser8]...
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUserA')  CREATE USER [MyDomain\AUserA]...
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUserB')  CREATE USER [MyDomain\AUserB]...
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUserC')  CREATE USER [MyDomain\AUserC]…

Tim 出色的开始(第 2 版)的修订脚本:-)

    #create new output file
new-item "C:\temp\test2.sql" -Force
foreach ($line in (get-content "C:\temp\test.sql"))
{
    if ($line.Contains("@@Placeholder@@"))
    {
    #Find the the user between the brackets and remove the brackets.
    $user = ([regex]"\[.+?\]").Match($line).value.replace("[","").replace("]","")
    #Replace PlaceHolder with user pulled from the brackets and write to new file
    $line -replace '@@Placeholder@@', $user | out-file "C:\temp\test2.sql" -Append 
    }
    else
    {
        out-file  "C:\temp\test2.sql" -Append -InputObject $line
    }

}

...必须调整正则表达式(降低宽松程度)并输出所有其余的脚本行。