Set-Content добавляет новую строку (разрыв строки, CRLF) в конце моего файла

Мой исходный файл конфигурации (web1.config) не имеет дополнительной строки и при просмотре в блокноте (показывает все символы) выглядит следующим образом:

enter image description here

 <?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.6" />
    <httpRuntime targetFramework="4.6" />
  </system.web>
  <appSettings>
    <add key="myConnectionString" value="server=localhost;database=myDb;uid=myUser;password=myPass;" />
  </appSettings>
</configuration>

Теперь мне нужно применить скрипт, чтобы изменить имя моей базы данных на что-то похожее на:

 Move-Item "web1.config" "webtemp.config"
Get-Content "webtemp.config" | ForEach-Object {$_ -replace "database=myDb;", "database=newDb;"} |Set-Content "web1.config" -Force
Remove-Item "webtemp.config"
Write-Output('Settings Changed')

Итак, новый сгенерированный файл (web1.config) выглядит так:

enter image description here

Обратите внимание на дополнительную строку, добавленную в конце файла (которая совершенно не нужна). Я попробовал все другие варианты, такие как: - использование api вне файла - использование метода .net IO System.IO.StreamWriter - использование флага -nonewline ( он преобразует все 10 строк в одну строку) - используя разные параметры кодирования - попытался заменить \r\n на\r (не работает, так как снова set-content всегда генерирует crlf)

Я использую PowerShell v5.1.

Ответ 1

tl; dr (PSv5+; см. нижнюю часть для более старых версий):

(Get-Content webtemp.config) -replace "database=myDb;","database=newDb;" -join "'n" |
  Set-Content -NoNewline -Force web1.config

Примечание. Замените "'n" на "'r'n" если вы хотите, чтобы "'r'n" строки в стиле CRLF в стиле Windows, а не в "'r'n" строки только в LF в стиле Unix (PowerShell и многие утилиты могут обрабатывать оба варианта).


В PSv5+ Set-Content поддерживает переключатель -NoNewline, который инструктирует Set-Content не добавлять новую строку (разрыв строки) после каждого входного объекта. То же самое относится и к командлетам Add-Content и Out-File.

Другими словами: Set-Content -NoNewline напрямую объединяет строковые представления всех своих входных объектов:

> 'one', 'two' | Set-Content -NoNewline tmp.txt; Get-Content tmp.txt
onetwo

Если то, что вы передаете в Set-Content -NoNewline является отдельной строкой, в которой уже есть встроенные символы новой строки, вы можете использовать ее как есть и получить желаемый результат:

> "one'ntwo" | Set-Content -NoNewline tmp.txt; "$(Get-Content -Raw tmp.txt)?"
one
two?

Обратите внимание, что Get-Content -Raw читает файл в целом, как есть (кроме декодирования символов) и тот факт, что ? появляется сразу после two что означает, что в файле нет завершающего символа новой строки.

В вашем случае, поскольку вы обрабатываете входные строки одну за другой (через Get-Content без -Raw) и, следовательно, -Raw массив строк (строк), вы должны сначала объединить их с новой строкой в качестве разделителя - только между строками - и передать результат в Set-Content -NoNewline, как показано вверху; вот упрощенный пример:

> ('one', 'two') -join "'n" | Set-Content -NoNewline tmp.txt; "$(Get-Content -Raw tmp.txt)?"
one
two?

'one', 'two' - это двухэлементный строковый массив, который заменяет вашу построчную команду обработки.

Кодировка примечания:

В Windows PowerShell Set-Content создает файлы "ANSI" -encoded по умолчанию на основе устаревшей однобайтовой кодовой страницы вашей системы.
Для явного управления кодировкой используйте параметр -Encoding.


В PSv4- необходимо решение, использующее .NET Framework:

> [System.IO.File]::WriteAllText('tmp.txt', ('one', 'two') -join "'n"); "$(Get-Content -Raw tmp.txt)?"
one
two?

Обратите внимание, что [System.IO.File]::WriteAllText(), при отсутствии аргумента кодирования, по умолчанию использует UTF-8 без спецификации.
При необходимости [System.Text.Encoding] требуемый экземпляр кодировки [System.Text.Encoding] в качестве 3-го аргумента.

Ответ 2

Я этого никогда не замечал, поэтому быстро просмотрел и нашел:

set-content добавляет новые строки по умолчанию

Предлагаемое решение заключается в кодировании вашего содержимого в байтах, а затем использовании Set-Content с параметром -Encoding.

Set-Content test.txt ([byte[]][char[]] "test") -Encoding Byte

Я сам тестировал это, поэтому могу подтвердить, что это работает.