【问题标题】:Copy (append) multiple files into a single destination file将多个文件复制(附加)到一个目标文件中
【发布时间】:2022-02-23 02:39:40
【问题描述】:

我在使用 PowerShell 将多个 csv 文件合并为一个时遇到了一个奇怪的问题。我已经在 Windows 7 的 cmd 提示符下做了很多时间,但这里的输出只包含最早的文件。该命令是标准的东西:

C:\> copy *.csv output.csv

正如我所说,我得到的只是最早的 csv 复制到这个新文件中,但没有别的。这是 powershell 与简单 cmd 提示符的问题吗?

谢谢 迈克尔

【问题讨论】:

  • 在 PowerShell 控制台中,copyCopy-Item 命令的别名。 cmd copypowershell Copy-Item 不同。
  • 你可以试试Get-Content -Path '.\*.csv' >'.\output.csv'
  • @lit 可能有问题,因为Get-Content 也找到了output.csv。我建议:Get-Content -Path '*.csv' -Exclude 'output.csv' >'output.csv'
  • @zett42,是的,很好。
  • @lit,Get-Content 方法的问题在于您可能会无意中更改字符编码、换行符格式和尾随换行符或非尾随换行符状态(后两者可以通过 @987654333 避免@ 和 Set-Content -NoNewLine,然而)。

标签: powershell cmd merge append copy-item


【解决方案1】:

正如 cmets 中的 lit 所指出的,在 PowerShell 中,copyCopy-Item cmdlet 的内置别名,其功能不同于 cmd.exe 的内部 copy 命令:

  • 从 PowerShell 7.2.1 开始,Copy-Item支持将多个文件合并到一个目标文件中。请参阅底部部分以了解 - 可能会修改内容 - Get-Content 解决方案。

  • 目前,如果Copy-Item-Destination 参数(第二个位置参数,在您的情况下为output.csv)是一个文件,则所有-Path 参数(第一个位置参数, *.csv 在您的情况下)被按顺序复制到同一个目标文件 - 换句话说:匹配通配符模式@987654341的最后一个文件@“wins”,而output.csv 只是它本身的副本 - 请参阅GitHub issue #12805 进行讨论。


要使用cmd.execopy 命令合并输入文件以形成目标文件,通过cmd /c 调用强>:

cmd /c 'copy /y /b *.csv output.csv'

警告:正如aschipfl's helpful answer 中所讨论的,preexisting output.csv 文件的处理方式取决于output.csv 是否恰好是第一个 文件是否与通配符模式*.csv 匹配。要么使用那里提出的解决方法,要么只是确保事先不存在 output.csv 文件。

注意添加:

  • /y,如果目标文件已经存在则禁止确认提示

  • /b,以 binary 模式复制,防止出现“EOF 字符”(Substitute character0x1a,您可以使用 Ctrl-Z) 被附加到目标文件。

顺便说一句:在类 Unix 平台上你可以使用sh -c 'cat *.csv > output.csv',但是你总是必须首先确保没有预先存在的output.csv文件,因为这会导致文件无限增长。


或者,您可以使用Get-Content cmdlet 来合并多个文本文件,正如问题的 cmets 中 lit 和 zett42 提出的那样,但 这样做可以更改字符编码和换行符格式,在给定的用例中可能需要也可能不需要:

# !! Caveat: may change character encoding and newline format.
# !! -Encoding utf8 used as an example.
Get-Content *.csv -Exclude output.csv | Set-Content -Encoding utf8 output.csv
  • Get-Content,假设它正确解释文本文件的编码(基于文件的 BOM,如果存在,否则假设默认值),将文件的行加载到 .NET 字符串中,有关文件字符编码的信息是没有保留。

  • 类似地,Out-File(及其有效别名>)和Set-Content 等文件写入 cmdlet 对.NET 输入字符串进行操作并使用默认值 保存到文件时的编码 - 尽管可以通过 -Encoding 参数请求不同的编码。

    • 换句话说:如果您的输入文件有一个一致的、非默认的编码,您想在目标文件中保留该编码,您 (a) 需要知道该编码是什么,并且 (b) 通过 @987654363 请求使用它@。

    • 注意:Windows PowerShell 默认为Get-ContentSet-Content 的系统旧版ANSI 代码页,以及Out-File / > 的UTF-16LE(“Unicode”)。相比之下,PowerShell (Core) 7+ 现在值得称道的是使用(BOM-less)UTF-8,在所有 cmdlet 中始终如一。

  • 此外,由于默认情况下Get-Content逐行读取文件,因此有关特定换行格式的信息会丢失。然后,文件保存 cmdlet 使用 platform-native 换行符序列(在 Windows 上为 CRLF ("`r`n"),在类 Unix 平台上为 LF ("`n")),因此目标文件可能会结束使用不同的换行符格式。此外,关于给定文件是否有尾随换行符的信息也会丢失。

    • 以必须将每个文件全部读入内存为代价(这通常不是文本文件的问题),您可以通过将 Get-Content -RawSet-Content -NoNewLine 组合来保留原始换行格式和尾随换行状态:

      Get-Content -Raw *.csv -Exclude output.csv | 
        Set-Content -Encoding utf8 -NoNewLine output.csv
      

至于用例

  • 您可以将Get-Content + Set-Content 用于以下一种或多种情况:

    • 如果你的输入文件是文本文件,使用可变字符编码(所有这些Get-Content需要能够识别),以便创建一个一致的编码的目标文件。

    • 同样,即使输入文件具有相同编码,您也可以选择转码内容,即为目标文件选择不同的编码。 p>

    • 如果您想将换行符格式标准化为平台原生格式,并且可能还想确保尾随换行符的存在。

  • 否则,如果输入文件的内容必须按原样保留 - 对于 二进制 文件尤其如此 - 请使用 cmd /c 'copy ...' 方法

    • 在 PowerShell 中解决此问题需要大量使用较低级别的 .NET API。

【讨论】:

    【解决方案2】:

    cmd.execopy command 在您指定多个源文件和单个目标文件时连接文件,就像您一样,所以命令行:

    copy *.csv output.csv
    

    应该按照文件系统报告的顺序连接所有与源模式*.csv 匹配的文件(因此在 NTFS 上按字母顺序排列)。您应该在末尾附加 /B 选项以将目标文件标记为二进制文件,从而避免附加文件结尾字符 (0x1A)。

    但只要output.csv 存在,结果取决于搜索返回的位置对源掩码*.csv

    • output.csv 是第一项:所有其他文件都附加到output.csv,前提是确认了覆盖提示,或者添加了/Y 开关;
    • output.csv 不是第一个:所有其他文件都连接到output.csv,原来的output.csv 文件似乎被忽略了;

    这已经在 Windows 10 上进行了测试,还考虑了 copy 的控制台输出,它列出了所有复制的文件,但不包括 output.csv,除非它位于第一个位置。

    因此要获得独立于文件系统的结果,您可以简单地明确指定第一项(即nul,空设备,不提供数据)并使用copy支持的+运算符:

    copy /Y nul + *.csv output.csv /B
    

    这可确保可能已经存在的文件 output.csv 永远不会是要复制的文件列表中的第一个文件,因此在连接时会被忽略。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-23
      • 2017-05-02
      • 2021-03-21
      • 2023-03-11
      • 2017-11-16
      • 1970-01-01
      • 2019-04-03
      • 2020-03-19
      相关资源
      最近更新 更多