40

编者注:从 OP 的后续评论来看,这个问题的要点是: 如何在 PowerShell 中将具有 CRLF(Windows 样式)行尾的文件转换为仅 LF(Unix 样式)文件?

这是我的powershell脚本:

 $original_file ='C:\Users\abc\Desktop\File\abc.txt'
 (Get-Content $original_file) | Foreach-Object {
 $_ -replace "'", "2"`
-replace '2', '3'`
-replace '1', '7'`
-replace '9', ''`
-replace "`r`n",'`n'
} | Set-Content "C:\Users\abc\Desktop\File\abc.txt" -Force

使用此代码,我可以将 2 替换为 3,将 1 替换为 7,将 9 替换为空字符串。我无法仅用换行符替换回车换行符。但这不起作用。

4

7 回答 7

47

这是从 Windows PowerShell v5.1 / PowerShell Core v6.2.0 开始的最新答案

  • Andrew Savinykh 命运多舛的答案,尽管被接受,但在撰写本文时存在根本缺陷(我确实希望它得到修复 - 评论中有足够的信息 - 以及编辑历史记录 - 这样做)。

  • Ansgar Wiecher 的有用答案 效果很好,但需要直接使用 .NET Framework(并将整个文件读入内存,尽管可以更改)。直接使用 .NET Framework 本身不是问题,但对于新手来说更难掌握,而且通常很难记住。

  • PowerShell Core未来版本将有一个带有参数的 cmdlet,以允许就地更新具有特定换行样式的文本文件,正如GitHub 上所讨论的那样。
    Convert-TextFile-LineEnding

PSv5+中,PowerShell-native 解决方案现在是可能的,因为Set-Content现在支持-NoNewlineswitch,这可以防止不希望地附加平台-native 换行符[1]

# Convert CRLFs to LFs only.
# Note:
#  * (...) around Get-Content ensures that $file is read *in full*
#    up front, so that it is possible to write back the transformed content
#    to the same file.
#  * + "`n" ensures that the file has a *trailing LF*, which Unix platforms
#     expect.
((Get-Content $file) -join "`n") + "`n" | Set-Content -NoNewline $file

以上依赖于逐行Get-Content读取使用CR-only、CRLF 和 LF-only换行符的任意组合的文本文件的能力。

注意事项

  • 您需要指定输出编码匹配输入文件,以便使用相同的编码重新创建它。上面的命令没有指定输出编码;为此,请使用-Encoding; 没有 -Encoding

    • Windows PowerShell中,您将获得“ANSI”编码,即系统的单字节 8 位传统编码,例如美国英语系统上的 Windows-1252。
    • PowerShell Core中,您将获得没有BOM 的UTF-8 编码
  • 输入文件的内容及其转换后的副本必须作为一个整体放入内存中,这对于大型输入文件可能会出现问题。

  • 如果写回输入文件的过程被中断,则存在文件损坏的风险。


[1] 事实上,如果有多个字符串要写,-NoNewline也不会在它们之间放置换行符;然而,在手头的情况下,这无关紧要,因为只写入了一个字符串。

于 2018-02-22T03:25:18.133 回答
42

您尚未指定版本,我假设您使用的是 Powershell v3。

尝试这个:

$path = "C:\Users\abc\Desktop\File\abc.txt"
(Get-Content $path -Raw).Replace("`r`n","`n") | Set-Content $path -Force

编者注:正如 mike z 在评论中指出的那样,Set-Content附加一个尾随 CRLF,这是不受欢迎的。验证:'hi' > t.txt; (Get-Content -Raw t.txt).Replace("`r`n","`n") | Set-Content t.txt; (Get-Content -Raw t.txt).EndsWith("`r`n"),产生$True

请注意,这会将整个文件加载到内存中,因此如果您想处理大文件,您可能需要不同的解决方案。

更新

这可能适用于 v2(抱歉无处测试):

$in = "C:\Users\abc\Desktop\File\abc.txt"
$out = "C:\Users\abc\Desktop\File\abc-out.txt"
(Get-Content $in) -join "`n" > $out

编者注:请注意,此解决方案(现在)写入不同的文件,因此不等同于(仍有缺陷的)v3 解决方案。(一个不同的文件旨在避免 Ansgar Wiechers 在评论中指出的陷阱:在执行开始之前使用> 截断目标文件)。不过,更重要的是:此解决方案也附加了尾随 CRLF,这可能是不希望的。用 验证,产生。'hi' > t.txt; (Get-Content t.txt) -join "`n" > t.NEW.txt; [io.file]::ReadAllText((Convert-Path t.NEW.txt)).endswith("`r`n")$True

不过,关于被加载到内存中的保留相同。

于 2013-10-02T00:06:36.750 回答
29

不会附加虚假 CR-LF 的替代解决方案:

$original_file ='C:\Users\abc\Desktop\File\abc.txt'
$text = [IO.File]::ReadAllText($original_file) -replace "`r`n", "`n"
[IO.File]::WriteAllText($original_file, $text)
于 2013-10-02T08:09:07.907 回答
2

下面是我用于递归转换所有文件的脚本。您可以指定要排除的文件夹或文件。

$excludeFolders = "node_modules|dist|.vs";
$excludeFiles = ".*\.map.*|.*\.zip|.*\.png|.*\.ps1"

Function Dos2Unix {
    [CmdletBinding()]
    Param([Parameter(ValueFromPipeline)] $fileName)

    Write-Host -Nonewline "."

    $fileContents = Get-Content -raw $fileName
    $containsCrLf = $fileContents | %{$_ -match "\r\n"}
    If($containsCrLf -contains $true)
    {
        Write-Host "`r`nCleaing file: $fileName"
        set-content -Nonewline -Encoding utf8 $fileName ($fileContents -replace "`r`n","`n")
    }
}

Get-Childitem -File "." -Recurse |
Where-Object {$_.PSParentPath -notmatch $excludeFolders} |
Where-Object {$_.PSPath -notmatch $excludeFiles} |
foreach { $_.PSPath | Dos2Unix }
于 2020-04-22T10:47:05.833 回答
2

根据@ricky89 和@mklement0 上面的示例添加另一个版本,但几乎没有改进:

处理脚本:

  • 当前文件夹中的 *.txt 文件
  • 用 CRLF 替换 LF(Unix 到 Windows 行尾)
  • 将生成的文件保存到 CR-to-CRLF 子文件夹
  • 在 100MB+ 文件上测试,PS v5;

LF 到 CRLF.ps1:

# get current dir
$currentDirectory = Split-Path $MyInvocation.MyCommand.Path -Parent

# create subdir CR-to-CRLF for new files
$outDir = $(Join-Path $currentDirectory "CR-to-CRLF")
New-Item -ItemType Directory -Force -Path $outDir | Out-Null

# get all .txt files
Get-ChildItem $currentDirectory -Force | Where-Object {$_.extension -eq ".txt"} | ForEach-Object {
  $file = New-Object System.IO.StreamReader -Arg $_.FullName
  # Resulting file will be in CR-to-CRLF subdir
  $outstream = [System.IO.StreamWriter] $(Join-Path  $outDir $($_.BaseName + $_.Extension))
  $count = 0 
  # read line by line, replace CR with CRLF in each by saving it with $outstream.WriteLine
  while ($line = $file.ReadLine()) {
        $count += 1
        $outstream.WriteLine($line)
    }
  $file.close()
  $outstream.close()
  Write-Host ("$_`: " + $count + ' lines processed.')
}
于 2017-05-18T05:30:09.670 回答
0

以下将能够快速处理非常大的文件。

$file = New-Object System.IO.StreamReader -Arg "file1.txt"
$outstream = [System.IO.StreamWriter] "file2.txt"
$count = 0 

while ($line = $file.ReadLine()) {
      $count += 1
      $s = $line -replace "`n", "`r`n"
      $outstream.WriteLine($s)
  }

$file.close()
$outstream.close()

Write-Host ([string] $count + ' lines have been processed.')
于 2015-09-06T14:39:46.693 回答
0

对于 CMD 一行 LF-only:

powershell -NoProfile -command "((Get-Content 'prueba1.txt') -join \"`n\") + \"`n\" | Set-Content -NoNewline 'prueba1.txt'"

所以你可以创建一个 .bat

于 2020-12-14T17:06:24.913 回答