【问题标题】:Equivalent of *nix fold in PowerShellPowerShell 中 *nix fold 的等价物
【发布时间】:2015-03-30 09:07:57
【问题描述】:

今天我有几百个项目(来自 SQL 查询的 ID),需要将它们粘贴到另一个查询中以供分析师阅读。我需要 *nix fold 命令。我想取 300 行并将它们重新格式化为每行由空格分隔的多个数字。我会使用fold -w 100 -s

*nix 上的类似工具包括 fmtpar

在 Windows 上,是否有一种简单的方法可以在 PowerShell 中执行此操作?我希望 *-Format 命令行开关之一能够做到这一点,但我找不到它。我正在使用 PowerShell v4。

https://unix.stackexchange.com/questions/25173/how-can-i-wrap-text-at-a-certain-column-size

# Input Data
# simulate a set of 300 numeric IDs from 100,000 to 150,000
100001..100330 | 
    Out-File _sql.txt -Encoding ascii

# I want output like:
# 100001,  100002,  100003,  100004,  100005, ... 100010, 100011
# 100012,  100013,  100014,  100015,  100016, ... 100021, 100021
# each line less than 100 characters.

【问题讨论】:

  • 我试过-join ' ',输出确实换成了 100 个字符,但它换行在数字的中间。我想在空格处换行,这样数字的值就不会被换行所改变。
  • 刚刚找到vgoenka.tripod.com/unixscripts/fold.awk.txt,它可能与 Windows 上的 GIT cli 附带的 gawk.exe 一起使用。
  • @EmperorXLII 感谢您的链接。对于未来的读者:Emporer 链接中的解决方案使用 for 和本机类型,因此在 10,000-10,000,000 个项目的数据集上可能要快得多。众所周知,循环比管道快 10 倍(当数据集适合 RAM 时)。

标签: powershell powershell-4.0


【解决方案1】:

根据文件的大小,您可以将其全部读入内存,用空格连接,然后拆分为 100* 个字符或下一个空格

(Get-Content C:\Temp\test.txt) -join " " -split '(.{100,}?[ |$])' | Where-Object{$_}

该正则表达式查找 100 个字符,然后查找其后的第一个空格。该匹配然后是-split,但由于模式被包裹在括号中,匹配被返回而不是被丢弃。 Where 删除在匹配之间创建的空条目。

小样本证明理论

@"
134
124
1
225
234
4
34
2
42
342
5
5
2
6
"@.split("`n") -join " "  -split '(.{10,}?[ |$])' | Where-Object{$_}

以上内容尽可能分成 10 个字符。如果不能,数字仍然保留。示例基于我用头敲击键盘。

134 124 1 
225 234 4 
34 2 42 
342 5 5 
2 6

然后,您可以将其变成一个函数,以恢复您最可能寻找的简单性。它可以变得更好,但这并不是答案的重点。

Function Get-Folded{
    Param(
        [string[]]$Strings,
        [int]$Wrap = 50
    )
    $strings  -join " " -split "(.{$wrap,}?[ |$])" | Where-Object{$_}
}

再次使用示例

PS C:\Users\mcameron> Get-Folded -Strings (Get-Content C:\temp\test.txt) -wrap 40
"Lorem ipsum dolor sit amet, consectetur 
adipiscing elit, sed do eiusmod tempor incididunt 
ut labore et dolore magna aliqua. Ut enim 
ad minim veniam, quis nostrud exercitation 
... output truncated...

您可以看到它应该分成 40 个字符,但第二行更长。它在 40 之后的下一个空格处拆分以保留单词。

【讨论】:

  • 这似乎与 Unicode 字符有关。这个 变成了…。有没有办法解决这个问题?
  • 您能否将我链接到更大的示例进行测试并告诉我您的 PowerShell 版本?
  • 我正在运行 5.1 版。另一个例子是é变成é变成—
  • 我找到了解决方案。我将其更改为 Get-Content C:\Temp\test.txt -Encoding UTF8 并修复了它。
  • 哦。我正要说我没有看到问题。但是,如果您使用的是我的代码中的那个示例,那么是的,您会遇到这个问题。
【解决方案2】:

如果它是每行一个项目,并且您想将每 100 个项目连接到一个由空格分隔的单行,您可以将所有输出放入一个文本文件中,然后执行以下操作:

gc c:\count.txt -readcount 100 | % {$_ -join " "}

【讨论】:

  • 啊,我错过了关于角色的部分……我的只是每 100 个项目加入。其他答案可能更准确。
【解决方案3】:

当我看到这个时,我首先想到的是滥用 Format-Table 来执行此操作,主要是因为它知道在您指定宽度时如何正确断行。提出一个功能后,似乎其他解决方案更短,可能更容易理解,但我想我还是会继续发布这个解决方案:

function fold {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline)]
        $InputObject,
        [Alias('w')]
        [int] $LineWidth = 100,
        [int] $ElementWidth
    )

    begin {
        $SB = New-Object System.Text.StringBuilder

        if ($ElementWidth) {
            $SBFormatter = "{0,$ElementWidth} "
        }
        else {
            $SBFormatter = "{0} "
        }
    }

    process {
        foreach ($CurrentObject in $InputObject) {
            [void] $SB.AppendFormat($SBFormatter, $CurrentObject)
        }
    }

    end {
        # Format-Table wanted some sort of an object assigned to it, so I 
        # picked the first static object that popped in my head:
        ([guid]::Empty | Format-Table -Property @{N="DoesntMatter"; E={$SB.ToString()}; Width = $LineWidth } -Wrap -HideTableHeaders |
            Out-String).Trim("`r`n")
    }
}

使用它会产生如下输出:

PS C:\> 0..99 | Get-Random -Count 100 | fold
1 73 81 47 54 41 17 87 2 55 30 91 19 50 64 70 51 29 49 46 39 20 85 69 74 43 68 82 76 22 12 35 59 92 
13 3 88 6 72 67 96 31 11 26 80 58 16 60 89 62 27 36 37 18 97 90 40 65 42 15 33 24 23 99 0 32 83 14  
21 8 94 48 10 4 84 78 52 28 63 7 34 86 75 71 53 5 45 66 44 57 77 56 38 79 25 93 9 61 98 95          

PS C:\> 0..99 | Get-Random -Count 100 | fold -ElementWidth 2
74 89 10 42 46 99 21 80 81 82  4 60 33 45 25 57 49  9 86 84 83 44  3 77 34 40 75 50  2 18  6 66 13  
64 78 51 27 71 97 48 58  0 65 36 47 19 31 79 55 56 59 15 53 69 85 26 20 73 52 68 35 93 17  5 54 95  
23 92 90 96 24 22 37 91 87  7 38 39 11 41 14 62 12 32 94 29 67 98 76 70 28 30 16  1 61 88 43  8 63  
72                                                                                                  

PS C:\> 0..99 | Get-Random -Count 100 | fold -ElementWidth 2 -w 40
21 78 64 18 42 15 40 99 29 61  4 95 66  
86  0 69 55 30 67 73  5 44 74 20 68 16  
82 58  3 46 24 54 75 14 11 71 17 22 94  
45 53 28 63  8 90 80 51 52 84 93  6 76  
79 70 31 96 60 27 26  7 19 97  1 59  2  
65 43 81  9 48 56 25 62 13 85 47 98 33  
34 12 50 49 38 57 39 37 35 77 89 88 83  
72 92 10 32 23 91 87 36 41              

【讨论】:

    【解决方案4】:

    这就是我最终使用的。

    # simulate a set of 300 SQL IDs from 100,000 to 150,000
    100001..100330 | 
        %{ "$_, " } | # I'll need this decoration in the SQL script
        Out-File _sql.txt -Encoding ascii
    
    gc .\_sql.txt -ReadCount 10 | %{ $_ -join ' ' }
    

    感谢大家的努力和答案。我真的很惊讶在 Rohn Edward 的回答中,如果不使用 [guid]::Empty,就无法使用 Format-Table 来做到这一点。

    我的 ID 比我给出的示例更加一致,因此 Noah 使用 gc -ReadCount 是迄今为止这个特定数据集中最简单的解决方案,但将来我可能会使用 Matt 的答案或链接到的答案由皇帝在 cmets.

    【讨论】:

    • Format-Wide 在您有对象时有效。您可以使用 Select-String(我们的 grep)将每一行读取为 Match 对象。 sls .* .\_sql.txt | Format-Wide Line -Column 10。这会产生:100001, 100002, 100003, 100004, 100005, 100006, 100007, 100008, 100009, 100010, 。与原始答案相同的警告:仅当项目的大小几乎相同时才有效。
    【解决方案5】:

    我想出了这个:

    $array = 
    (@'
    1
    2
    3
    10
    11
    100
    101
    '@).split("`n") |
    foreach {$_.trim()}
    
    $array = $array * 40
    
    $SB = New-Object Text.StringBuilder(100,100)
    
    foreach ($item in $array) {
    
    Try { [void]$SB.Append("$item ") }
    
    Catch {
             $SB.ToString()
             [void]$SB.Clear()
             [Void]$SB.Append("$item ")
          }
    }    
    #don't forget the last line
    $SB.ToString()
    
    1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 
    1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 
    1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 
    1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 
    1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 
    1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 
    1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 1 2 3 10 11 100 101 
    

    可能没有您希望的那么紧凑,并且可能有更好的方法来做到这一点,但它似乎有效。

    【讨论】:

      猜你喜欢
      • 2010-09-06
      • 2011-09-12
      • 1970-01-01
      • 1970-01-01
      • 2016-07-25
      • 2011-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多