Заменить CRLF с помощью powershell

Примечание редактора: Судя по последующим комментариям ОП, суть этого вопроса такова:как преобразовать файл с окончаниями строк CRLF (в стиле Windows) в файл только для LF (в стиле Unix) в PowerShell?

Вот мой сценарий 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 с пустой строкой. Я не могу заменить перевод строки кареткой только перевод строки. Но это не работает.

7 ответов

Решение

Вы не указали версию, я предполагаю, что вы используете Powershell v3.

Попробуй это:

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

Примечание редактора: как указывает Майк 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. (Другой файл нацелен на то, чтобы избежать ловушки, на которую указывает Ансгар Вичерс в комментариях: использование > обрезает целевой файл перед началом выполнения). Что еще более важно: это решение также добавляет конечный 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 ,

Это же оговорка о загрузке в память, хотя.

Это ответ о состоянии дел с Windows PowerShell v5.1 / PowerShell Core v6.0.1:

  • Злополучный ответ Эндрю Савиных, несмотря на то, что он принят, на момент написания этой статьи в корне ошибочен (я надеюсь, что он исправлен - в комментариях - и в истории редактирования - достаточно информации для этого).

  • Полезный ответ Ансгара Вихера работает хорошо, но требует непосредственного использования.NET Framework (и считывает весь файл в память, хотя это можно изменить). Непосредственное использование.NET Framework само по себе не является проблемой, но его трудно освоить новичкам, и его трудно запомнить в целом.

  • Будущая версия PowerShell Core (на момент написания этой статьи: v6.1.0) будет иметь
    Convert-TextFile командлет с -LineEnding параметр, позволяющий обновлять текстовые файлы на месте с определенным стилем новой строки, как обсуждалось на GitHub.

В PSv5+ теперь возможны собственные решения PowerShell, потому что Set-Content теперь поддерживает -NoNewline переключатель, который предотвращает нежелательное добавление нативной строки платформы [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 вы получите кодировку UTF-8 без спецификации.
  • Содержимое входного файла, а также его преобразованная копия должны помещаться в память в целом, что может быть проблематично с большими входными файлами.

  • Существует риск повреждения файла, если что-то пойдет не так, прежде чем новый контент может быть (полностью) записан обратно во входной файл.


[1] На самом деле, если есть несколько строк для записи, -NoNewline также не помещает новую строку между ними; однако в данном случае это не имеет значения, поскольку записывается только одна строка.

Альтернативное решение, которое не добавит ложный 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)

Ниже мой скрипт для рекурсивного преобразования всех файлов. Вы можете указать папки или файлы, которые нужно исключить.

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

Добавление другой версии на основе приведенного выше примера с помощью @ricky89 и @mklement0 с небольшими улучшениями:

Скрипт для обработки:

  • *.txt файлы в текущей папке
  • заменить LF на CRLF (окончания строк от Unix до Windows)
  • сохранить полученные файлы в подпапку CR-to-CRLF
  • протестировано на 100 МБ + файлов, 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.')
}

Для CMD одна строка только LF:

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

так что вы можете создать .bat

Следующие смогут быстро обрабатывать очень большие файлы.

$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.')
Другие вопросы по тегам