【问题标题】:Powershell command to read between two points in a Loop and write to end of filePowershell命令在循环中的两点之间读取并写入文件末尾
【发布时间】:2021-12-13 07:26:31
【问题描述】:

有这个 mySQL 文件 (file.sql),我想复制起点和终点两点之间的文本并写入一个新的 .sql 文件。我在一个循环中需要这个,它将运行一个数组中的 N 个表并写入一个新的 .sql 文件。在每次迭代结束时,我需要添加新行然后将新表的文本附加到文件末尾的过程。

--DO NOT COPY
--DO NOT COPY
-- Table structure for table `address`
--

DROP TABLE IF EXISTS `address`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `address` (
`ENCODEDKEY` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`ADDRESSTYPE` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT 
NULL,
`CITY` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `address`
--

 LOCK TABLES `address` WRITE;
/*!40000 ALTER TABLE `address` DISABLE KEYS */;
INSERT INTO `address` VALUES ( 'ENCODEDKEY','ADDRESSTYPE','CITY');
/*!40000 ALTER TABLE `address` ENABLE KEYS */;
UNLOCK TABLES;
--DO NOT COPY
--DO NOT COPY
 
-- Table structure for table `client`
--
DROP TABLE IF EXISTS `client`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `client` (
`KEY` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`TYPE` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT 
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `client`
--

 LOCK TABLES `client` WRITE;
/*!40000 ALTER TABLE `client` DISABLE KEYS */;
INSERT INTO `client` VALUES ( 'KEY','TYPE');
/*!40000 ALTER TABLE `address` ENABLE KEYS */;
UNLOCK TABLES;
--DO NOT COPY
--DO NOT COPY

 

对于每个迭代,起点应该是 = 的确切字符串

表'find_tablename'的表结构

结束点应该是 = 的确切字符串

解锁表格;

我想复制/读取这 2 点之间的所有文本并写入一个新的 .sql 文件。这个过程应该在一个循环中,循环将运行 N 个表,并在添加新行后附加到同一个 .sql 文件。在这种情况下,它将针对表地址和客户端运行 2 次迭代。

有这个脚本在 PowerShell 中为地址表执行进程。我还考虑在 cmets 中使用如下所示的 Array,它允许我轻松添加/删除我需要的表,然后使用 FOR LOOP 进行迭代。我在 cmets 中的代码是我试图添加以执行此循环的代码。因此位置 tableList[num] 的值将替换 address

 # $tableList = New-Object -TypeName 'System.Collections.ArrayList';
 # $tableList.Add("address")
 # $tableList.Add("client")

 # for ($num = 1 ; $num -le $tableList.Count ; $num++)
 # {
$write = $false
$(switch -CaseSensitive -File ("file.sql") {
'-- Table structure for table `address`' { $write = $true; $_ }
'UNLOCK TABLES;' { if ($write) { $_; break; } }
default { if ($write) { $_ }}
 }) | Set-Content "new_file.sql"
 #}

【问题讨论】:

  • 真的是一个固定的表格数组,还是要提取所有个表格?您应该删除break 并重置$write。此外,您可以在开关中使用字符串扩展:"-- Table structure for table $tableName"
  • 是的,一个固定的表格数组。因此,该文件具有超过 200 个表的结构,其中客户和地址是其中的 2 个。假设我想提取其中 10 个表的结构,然后将我需要的这些表的名称添加到数组列表中,并且循环应该运行 10 次以允许进程写入同一个 .sql 文件。

标签: powershell


【解决方案1】:

基于现有的解决方案,我创建了这个函数:

function Select-TableSql {
    param (
        [Parameter(Mandatory, Position = 0)]
        [string]$Path,

        [Parameter(Mandatory, Position = 1)]
        [string[]]$TableNames
    )
    $write = $false
    switch -Regex -File ($Path) {
        '-- Table structure for table `(?<TableName>\w+)`' {
            if ($Matches.TableName -in $TableNames) {
                $write = $true
                $_
            }
        }
        'UNLOCK TABLES;' {
            if ($write) {
                $write = $false
                $_
            }
        }
        default {
            if ($write) {
                $_
            }
        }
    }
}

你可以这样使用它:

Select-TableSql file.sql client, address | Set-Content new_file.sql

注意:这里直接在文件上使用switch 的目的是因为它在大文件上比与Get-Content 的任何组合都快得多。

【讨论】:

    【解决方案2】:

    我不会在此使用switch,而是使用-split-replace 和最后-join 的组合:

    # read the original file as single multiline string
    $sql = Get-Content -Path 'D:\Test\SqlFile.sql' -Raw
    
    # 1) split the whole content on 'UNLOCK TABLES'
    # 2) keep only the text blocks that contain '-- Table structure for table'
    # 3) remove all text above '-- Table structure for table' from each text block. 
    # 4) Append the final 'UNLOCK TABLES;' sql command we've removed using the first split
    
    $result = $sql -split '(?ms)^UNLOCK TABLES;\s\r?\n' | Where-Object { $_ -match '-- Table structure for table' } | ForEach-Object {
        ($_ -replace '(?ms)^.*(-- Table structure for table .*)', '$1') + "UNLOCK TABLES;"
    }
    

    您现在可以将生成的数组保存为单个文件,但如果我正确理解问题,您希望将数组合并到一个新文件中

    $result -join "`r`n`r`n" | Set-Content -Path 'D:\Test\newSqlFile.sql'
    

    生成的文件将如下所示:

    -- Table structure for table `address`
    --
    
    DROP TABLE IF EXISTS `address`;
    /*!40101 SET @saved_cs_client     = @@character_set_client */;
    /*!40101 SET character_set_client = utf8 */;
    CREATE TABLE `address` (
    `ENCODEDKEY` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
    `ADDRESSTYPE` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT 
    NULL,
    `CITY` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    /*!40101 SET character_set_client = @saved_cs_client */;
    
    --
    -- Dumping data for table `address`
    --
    
     LOCK TABLES `address` WRITE;
    /*!40000 ALTER TABLE `address` DISABLE KEYS */;
    INSERT INTO `address` VALUES ( 'ENCODEDKEY','ADDRESSTYPE','CITY');
    /*!40000 ALTER TABLE `address` ENABLE KEYS */;
    UNLOCK TABLES;
    
    -- Table structure for table `client`
    --
    DROP TABLE IF EXISTS `client`;
    /*!40101 SET @saved_cs_client     = @@character_set_client */;
    /*!40101 SET character_set_client = utf8 */;
    CREATE TABLE `client` (
    `KEY` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
    `TYPE` varchar(256) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT 
     ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    /*!40101 SET character_set_client = @saved_cs_client */;
    
    --
    -- Dumping data for table `client`
    --
    
     LOCK TABLES `client` WRITE;
    /*!40000 ALTER TABLE `client` DISABLE KEYS */;
    INSERT INTO `client` VALUES ( 'KEY','TYPE');
    /*!40000 ALTER TABLE `address` ENABLE KEYS */;
    UNLOCK TABLES;
    

    编辑

    确实,我没有在您的问题中阅读有关仅过滤出一组固定表的任何内容,但这很容易实现:

    # the list of table names you want to retrieve from the file
    $tablesToExtract = 'address','client','someothertable' # etc.
    
    # read the original file as single multiline string
    $sql = Get-Content -Path 'D:\Test\SqlFile.sql' -Raw
    
    # 1) split the whole content on 'UNLOCK TABLES'
    # 2) keep only the text blocks that contain '-- Table structure for table'
    # 3) parse out the table name and check if that can be found in the $tablesToExtract array
    # 4) remove all text above '-- Table structure for table' from each text block. 
    # 5) Append the final 'UNLOCK TABLES;' sql command we've removed using the first split
    $result = $sql -split '(?ms)^UNLOCK TABLES;\s\r?\n' | Where-Object { $_ -match '-- Table structure for table' } | ForEach-Object {
        # check if the table is in the list
        $tableName = ([regex]'-- Table structure for table `(\w+)`').Match($_).Groups[1].Value
        if ($tablesToExtract -contains $tableName) {
            ($_ -replace '(?ms)^.*(-- Table structure for table .*)', '$1') + "UNLOCK TABLES;"
        }
    }
    

    您现在可以将生成的数组保存为单个文件,但如果我正确理解问题,您希望将数组合并到一个新文件中

    $result -join "`r`n`r`n" | Set-Content -Path 'D:\Test\newSqlFile.sql'
    

    【讨论】:

    • 您似乎错过了我需要像数组一样存储我要提取的固定数量的表名的问题部分。该数组将被传递给 -- 表 arraylist[tablename] 的表结构。如果我想提取所有表,您的解决方案会起作用,但我真的想从包含 200 多个表的巨大 .sql 文件中仅提取固定数量的表。
    • @JasonX 是的,确实.. 我已经重新阅读了您的问题,但仍然无法在其中找到该要求.. 无论如何,请查看我的编辑,其中代码现在只检索命名的表在您的固定字符串数组中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-26
    • 2018-09-24
    • 1970-01-01
    • 2020-01-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多