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

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

вот мой сценарий powershell:

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

С помощью этого кода я могу заменить 2 на 3, 1 на 7 и 9 с пустой строкой. Я не могу заменить возврат каретки перевод строки с линии подачи. Но это не работает.

5 ответов


вы не указали версию, я предполагаю, что вы используете 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. (Другой файл предназначен, чтобы избежать ловушки 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.

такое же резервирование о загрузке в память.


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

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

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

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

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

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

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

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

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

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


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


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

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

  • *.txt файлы в текущей папке
  • замените LF на CRLF (Unix на Windows-окончания строк)
  • сохранить результирующие файлы в подпапку CR-to-CRLF
  • протестировано на 100Mb + файлах, PS v5;

Если для возврата каретки и перевода строки.пс1:

# 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.')
}

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

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