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

Замечание редактора. Судя по более поздним комментариям OP, суть этого вопроса: как вы можете преобразовать файл с концами строк CRLF (в стиле Windows) в файл LF-only (Unix-style) в 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 пустой строкой. Я не могу заменить подачу линии возврата каретки только с помощью линии. Но это не работает.

Ответ 1

Вы не указали версию, я предполагаю, что вы используете 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")'hi' > t.txt; (Get-Content -Raw t.txt).Replace("'r'n","'n") | Set-Content t.txt; (Get-Content -Raw t.txt).EndsWith("'r'n")'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")'hi' > t.txt; (Get-Content t.txt) -join "'n" > t.NEW.txt; [io.file]::ReadAllText((Convert-Path t.NEW.txt)).endswith("'r'n")'hi' > t.txt; (Get-Content t.txt) -join "'n" > t.NEW.txt; [io.file]::ReadAllText((Convert-Path t.NEW.txt)).endswith("'r'n"), который дает $True.

Тем не менее, о том, что он загружен в память.

Ответ 2

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

Ответ 3

Это ответ на вопрос о состоянии Windows PowerShell v5.1/PowerShell Core v6.0.1:

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

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

  • Будущая версия PowerShell Core (текущая на данный момент: v6.0.2) будет иметь
    Командлет 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.

Предостережения:

  • Вам нужно указать выходную кодировку в соответствии с входным файлом, чтобы воссоздать его с той же кодировкой. В приведенной выше команде НЕ указывается выходная кодировка; для этого используйте -Encoding; без -Encoding:

    • В Windows PowerShell вы получите кодировку "ANSI", системную однобайтную 8-битную устаревшую кодировку, такую как Windows-1252 на англо-английских системах.
    • В PowerShell Core вы получите кодировку UTF-8 без спецификации.
  • Содержимое входного файла, а также его преобразованная копия должны вписываться в память в целом, что может быть проблематично для больших входных файлов.

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


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

Ответ 4

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

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

  • *.txt файлы в текущей папке
  • замените LF на CRLF (окончание строк 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.')
}

Ответ 5

Ниже вы сможете обрабатывать очень большие файлы быстро.

$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.')