使用powershell更换CRLF

使用powershell更换CRLF,powershell,replace,newline,eol,Powershell,Replace,Newline,Eol,编者按:根据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'` -re

编者按:根据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
有了这段代码,我可以用3替换2,用7替换1,用空字符串替换9。 我无法仅用换行符替换回车换行符。
但是这不起作用。

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

试试这个:

$path = "C:\Users\abc\Desktop\File\abc.txt"
(Get-Content $path -Raw).Replace("`r`n","`n") | Set-Content $path -Force
编者按:正如mike z在评论中指出的那样,
设置内容
附加了一个不需要的尾部CRLF。验证:
'hi'>t.txt;(Get Content-Raw t.txt);(Get Content-Raw t.txt).EndsWith(“'r`n”)
,它生成
$True

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

更新

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

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


关于加载到内存的保留相同。

不会附加虚假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)

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

$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.')
基于@ricky89和@mklement0的上述示例添加了另一个版本,但改进很少:

要处理的脚本:

  • *当前文件夹中的.txt文件
  • 将LF替换为CRLF(Unix到Windows的行尾)
  • 将结果文件保存到CR到CRLF子文件夹
  • 在100MB+文件上测试,PS v5
LF-to-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.')
}

这是自Windows PowerShell v5.1/PowerShell Core v6.2.0以来的一个“联盟状态”答案:

  • 尽管是公认的,但在撰写本文时,它存在根本性的缺陷(我确实希望它得到修复——评论中有足够的信息——编辑历史中也有足够的信息)

  • 运行良好,但需要直接使用.NET Framework(并将整个文件读取到内存中,尽管这是可以更改的)。直接使用.NET框架本身并不是问题,但对于新手来说更难掌握,一般来说也很难记住

  • PowerShell Core的未来版本将具有
    Convert TextFile
    cmdlet带有
    -LineEnding
    参数,以允许使用特定换行符样式就地更新文本文件,如前所述

PSv5+中,PowerShell本机解决方案现在是可能的,因为
设置内容
现在支持
-NoNewline
开关,可防止不必要的平台本机换行[1] :

上述功能依赖于
Get Content
逐行读取使用仅限CR、CRLF和仅限LF新行任意组合的文本文件的能力

注意事项

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

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

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



[1] 事实上,如果有多个字符串要写,
-NoNewline
也不会在它们之间换行;然而,在本例中,这是不相关的,因为只写入了一个字符串。

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

$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 }

仅适用于CMD一行LF:

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

因此,您可以创建一个.bat

设置从管道到文件的内容写入。管道中的每一项都写在新行上。这几乎可以正常工作<代码>设置内容
仍将在末尾插入一个额外的CR/LF。太好了,我更新了powershell v3,您的代码正常工作,但它仍然会像mike提到的那样在末尾保留CR/LF。我只希望所有LF和无CR/LF您对PowerShell v2的建议将删除文件内容,因为重定向将在子shell读取之前创建一个新的空文件。请将其删除。PowerShell v2和v3中的行为相同。使用重定向操作符在文件被
Get Content
读取之前截断文件。PSv5+为后续CRLF问题提供了解决方案:
Set Content-NoNewline
。可以使用
|Out file…
(或
|Set Content…
)来避免使用
截断输出文件。做得很好(也适用于v2)。重复使用相对路径的提示:使用
(转换路径$original\u file)
转换相对路径
$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 }
powershell -NoProfile -command "((Get-Content 'prueba1.txt') -join \"`n\") + \"`n\" | Set-Content -NoNewline 'prueba1.txt'"