Как вывести текст без новой строки в PowerShell?


149

Я хочу, чтобы мой сценарий PowerShell напечатал что-то вроде этого:

Enabling feature XYZ......Done

Скрипт выглядит примерно так:

Write-Output "Enabling feature XYZ......."
Enable-SPFeature...
Write-Output "Done"

Но Write-Outputвсегда печатает новую строку в конце, поэтому мой вывод не находится в одной строке. Есть ли способ сделать это?


Ответы:


165

Write-Host -NoNewline "Включение функции XYZ ......."


70
Проголосовали против, потому что пример OP специально использует Write-Output, который имеет совершенно другие функции, чем Write-Host. Читатели должны обратить внимание на это большое несоответствие, прежде чем копировать / вставлять ответ.
NathanAldenSr

5
Я согласен с @NathanAldenSr, Write-Host не помогает, если вы пытаетесь выполнить вывод в файл и т. Д.
stevethethread 03

6
Write-Hostпочти никогда не бывает правильным ответом. Это то же самое, что и >/dev/ttyв Unixland.
Марк Рид,

2
Write-Progressможет быть уместным в некоторых случаях, см. пример ниже.
Thomas

14
Проголосовали против, потому что в примере OP специально используется Write-Output. Write-Outputне имеет -NoNewLineпараметра.
Slogmeister Extraordinaire

50

К сожалению, как отмечено в нескольких ответах и ​​комментариях, Write-Hostможет быть опасным и не может быть передан другим процессам и Write-Outputне имеет -NoNewlineфлага.

Но эти методы представляют собой способы "* nix" для отображения прогресса, способ "PowerShell", по-видимому, таков Write-Progress: он отображает панель в верхней части окна PowerShell с информацией о ходе выполнения, доступной начиная с PowerShell 3.0, см. Руководство для Детали.

# Total time to sleep
$start_sleep = 120

# Time to sleep between each notification
$sleep_iteration = 30

Write-Output ( "Sleeping {0} seconds ... " -f ($start_sleep) )
for ($i=1 ; $i -le ([int]$start_sleep/$sleep_iteration) ; $i++) {
    Start-Sleep -Seconds $sleep_iteration
    Write-Progress -CurrentOperation ("Sleep {0}s" -f ($start_sleep)) ( " {0}s ..." -f ($i*$sleep_iteration) )
}
Write-Progress -CurrentOperation ("Sleep {0}s" -f ($start_sleep)) -Completed "Done waiting for X to finish"

И чтобы взять пример OP:

# For the file log
Write-Output "Enabling feature XYZ"

# For the operator
Write-Progress -CurrentOperation "EnablingFeatureXYZ" ( "Enabling feature XYZ ... " )

Enable-SPFeature...

# For the operator
Write-Progress -CurrentOperation "EnablingFeatureXYZ" ( "Enabling feature XYZ ... Done" )

# For the log file
Write-Output "Feature XYZ enabled"

3
Думаю, это лучшее решение для отображения статуса. Если вам нужен журнал или что-то, что вам нужно, с переносом строки Write-Output.
fadanner

1
Согласовано, плюс точка прогрессивного отображения - это просто «изобразить» для живой установки, нет смысла помещать это в файлы журнала: напечатайте «начать делать что-то», затем «сделать что-то»
Томас

11

Хотя это может не работать в вашем случае (поскольку вы предоставляете пользователю информативный вывод), создайте строку, которую вы можете использовать для добавления вывода. Когда пришло время вывести его, просто выведите строку.

Игнорируя, конечно, то, что этот пример глуп в вашем случае, но полезен по идее:

$output = "Enabling feature XYZ......."
Enable-SPFeature...
$output += "Done"
Write-Output $output

Отображает:

Enabling feature XYZ.......Done

1
Это может сработать в конкретном приведенном примере, но по-прежнему создается дополнительный перевод строки Write-Output. Разумное решение, но не решение.
Slogmeister Extraordinaire

Команда Write-Output всегда выводит новую строку в конце. С помощью этого командлета это
невозможно

7
Дело не в этом, поскольку весь вывод появляется после установки функции.
majkinetor

11
Я не понимаю, кто дает более 1 апвота на этот вопрос, потому что это не суть, поскольку весь вывод появляется ПОСЛЕ установки функции
maxkoryukov

1
«Игнорирование, конечно, того, что этот пример глуп в вашем случае, но полезен по концепции»
shufler

6

Да, поскольку в других ответах есть состояния, это невозможно сделать с помощью записи-вывода. Если PowerShell не работает, обратитесь к .NET, здесь есть даже пара ответов .NET, но они сложнее, чем должны быть.

Просто используйте:

[Console]::Write("Enabling feature XYZ.......")
Enable-SPFeature...
Write-Output "Done"

Это не самый чистый PowerShell, но он работает.


6
Проголосовали против, потому что это ведет себя точно так же Write-Host, только люди этого не ожидают.
JBert

4

Для записи в файл вы можете использовать массив байтов. В следующем примере создается пустой ZIP-файл, в который вы можете добавлять файлы:

[Byte[]] $zipHeader = 80, 75, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
[System.IO.File]::WriteAllBytes("C:\My.zip", $zipHeader)

Или используйте:

[Byte[]] $text = [System.Text.Encoding]::UTF8.getBytes("Enabling feature XYZ.......")
[System.IO.File]::WriteAllBytes("C:\My.zip", $text)

Это отличный пример!
Питер Мортенсен


2

Проблема, с которой я столкнулся, заключалась в том, что Write-Output фактически прерывает вывод при использовании PowerShell v2, по крайней мере, для stdout. Я безуспешно пытался записать XML-текст в стандартный вывод, потому что он был бы жестко привязан к символу 80.

Обходной путь заключался в использовании

[Console]::Out.Write($myVeryLongXMLTextBlobLine)

Это не было проблемой в PowerShell v3. Кажется, что запись-вывод там работает правильно.

В зависимости от того, как вызывается сценарий PowerShell, вам может потребоваться использовать

[Console]::BufferWidth =< length of string, e.g. 10000)

прежде чем писать в stdout.


2
Ведет себя как Write-Host, но хуже всего. Например, вы не можете передать его в файл.
majkinetor

2

Кажется, в PowerShell это невозможно сделать. Все предыдущие ответы неверны, потому что они не ведут себя так, как Write-Outputведет себя, а больше похоже на то, Write-Hostчто в любом случае не имеет этой проблемы.

Кажется, что закрывающее решение используется Write-Hostс -NoNewLineпараметром. Вы не можете передать это по конвейеру, что обычно является проблемой, но есть способ переопределить эту функцию, как описано в Write-Host => Export to a file , поэтому вы можете легко заставить ее принимать параметр для выходного файла. Это еще далеко не лучшее решение. При Start-Transcriptэтом более удобные, но командлет имеет проблемы с родными приложениями.

Write-Outputпросто не может делать то, что вам нужно в общем контексте.


2

Write-Hostужасен, разрушитель миров, но вы можете использовать его только для отображения прогресса пользователю при использовании Write-Outputдля входа в систему (не то, чтобы OP запрашивал запись).

Write-Output "Enabling feature XYZ" | Out-File "log.txt" # Pipe to log file
Write-Host -NoNewLine "Enabling feature XYZ......."
$result = Enable-SPFeature
$result | Out-File "log.txt"
# You could try{}catch{} an exception on Enable-SPFeature depending on what it's doing
if ($result -ne $null) {
    Write-Host "complete"
} else {
    Write-Host "failed"
}

1
В зависимости от ваших потребностей в индикации прогресса также есть Write-Progress.
chwarr

1

Вы просто не можете заставить PowerShell опустить эти надоедливые символы новой строки ... Нет сценария или командлета, которые бы это сделали. Конечно, Write-Host - это полная ерунда, потому что вы не можете перенаправить / перенаправить с него!

Тем не менее, вы можете написать свой собственный EXE-файл для этого, что я объяснил, как это сделать, в вопросе о переполнении стека Как вывести что-то в PowerShell .


2
Неверная информация. Как прекрасно ответили Шей и Джей, просто добавьте -NoNewline в качестве первого аргумента.
Дэвид в HotspotOffice,

2
Возможно, сейчас так обстоит дело с @DavidatHotspotOffice, но когда я последний раз касался окна Windows (более года назад), которое не работало, вы не могли перенаправить / перенаправить с Write-Host. Честно говоря, у меня не было ни малейшего терпения к POSH или .NET, я ушел через несколько месяцев и вернулся на землю unix. смешно
samthebest

3
@DavidatHotspotOffice - Вообще-то он прав. Для Write-Output нет аргумента «NoNewLine», о чем и спрашивалось в исходном вопросе. Кажется, есть несколько веских причин для использования записи-вывода, поэтому этот ответ имеет смысл. jsnover.com/blog/2013/12/07/write-host-considered-harmful
Джеймс Раскин,

Проголосовали против, потому что вопрос требует решения "в PowerShell". Написание внешнего EXE - это не «в PowerShell».
Slogmeister Extraordinaire

1
@SlogmeisterExtraordinaire Это невозможно в PowerShell, поэтому мой ответ разумный. Вы просто голосуете против, потому что вам так грустно, что вам приходится работать с худшей в мире операционной системой, которая имеет худшую оболочку в мире.
samthebest

1

Ответ шуфлера правильный. Другими словами: вместо передачи значений в Write-Output с использованием ФОРМЫ ARRAY FORM,

Write-Output "Parameters are:" $Year $Month $Day

или эквивалент несколькими вызовами Write-Output,

Write-Output "Parameters are:"
Write-Output $Year
Write-Output $Month
Write-Output $Day
Write-Output "Done."

сначала объедините свои компоненты в ПЕРЕМЕННУЮ СТРОКУ:

$msg="Parameters are: $Year $Month $Day"
Write-Output $msg

Это предотвратит промежуточные CRLF, вызванные многократным вызовом Write-Output (или ARRAY FORM), но, конечно, не подавит окончательный CRLF командлета Write-Output. Для этого вам придется написать свой собственный командлет, использовать один из других запутанных обходных путей, перечисленных здесь, или подождать, пока Microsoft решит поддержать -NoNewlineопцию Write-Output.

Ваше желание предоставить на консоль текстовый индикатор выполнения (например, «....») вместо записи в файл журнала также должно быть удовлетворено с помощью Write-Host. Вы можете выполнить и то, и другое, собрав текст сообщения в переменную для записи в журнал И используя Write-Host для отображения прогресса на консоли. Эту функциональность можно объединить в ваш собственный командлет для максимального повторного использования кода.


Я предпочитаю этот ответ другим. Если вы вызываете свойства объектов, вы не можете заключать их в кавычки, поэтому я использовал: Write-Output ($ msg = $ MyObject.property + "Some text, который я хочу включить" + $ Object.property)
Льюис

2
@Lewis Вы, конечно, можете включить свойства объекта в строку! Используйте выражение $ (), чтобы окружить любую переменную, например «$ ($ MyObject.Property) Some text I want to include $ ($ Object.property)»
shufler

Это может сработать в конкретном приведенном примере, но по-прежнему создается дополнительный перевод строки Write-Output, вы просто не можете его видеть, потому что это последнее, что написано. Разумное решение, но не решение. Возможно, что-то потребляет результирующий вывод, но не может обработать завершающую новую строку.
Slogmeister Extraordinaire

1
Не верно. Решение не может быть выполнено с помощью одной команды.
majkinetor

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

1

Я обманул, но считаю, что это единственный ответ, отвечающий всем требованиям. А именно, это позволяет избежать замыкающего CRLF, предоставляет место для завершения другой операции и при необходимости правильно перенаправляет на стандартный вывод.

$c_sharp_source = @"
using System;
namespace StackOverflow
{
   public class ConsoleOut
   {
      public static void Main(string[] args)
      {
         Console.Write(args[0]);
      }
   }
}
"@
$compiler_parameters = New-Object System.CodeDom.Compiler.CompilerParameters
$compiler_parameters.GenerateExecutable = $true
$compiler_parameters.OutputAssembly = "consoleout.exe"
Add-Type -TypeDefinition $c_sharp_source -Language CSharp -CompilerParameters $compiler_parameters

.\consoleout.exe "Enabling feature XYZ......."
Write-Output 'Done.'

0

Следующее вернет курсор в начало предыдущей строки. Вы можете поместить его в правильное горизонтальное положение (используя $ pos.X, чтобы переместить его в сторону):

$pos = $host.ui.RawUI.get_cursorPosition()
$pos.Y -= 1
$host.UI.RawUI.set_cursorPosition($Pos)

Ваш текущий вывод превышает 27 пробелов, поэтому $ pos.X = 27 может работать.


Это не имеет ничего общего с выводом в файл.
Slogmeister Extraordinaire

Это тоже не так уж и плохо. Если вы это сделаете, он может выдать правильный результат $pos.X. Проблема в том, что если вы отправите его в файл, появятся две отдельные строки.
majkinetor

0

Это может быть не очень элегантно, но делает именно то, что просил OP. Обратите внимание, что ISE портит StdOut, поэтому вывода не будет. Чтобы увидеть, как работает этот сценарий, его нельзя запустить в ISE.

$stdout=[System.Console]::OpenStandardOutput()
$strOutput="Enabling feature XYZ... "
$stdout.Write(([System.Text.Encoding]::ASCII.GetBytes($strOutput)),0,$strOutput.Length)
Enable-SPFeature...
$strOutput="Done"
$stdout.Write(([System.Text.Encoding]::ASCII.GetBytes($strOutput)),0,$strOutput.Length)
$stdout.Close()

1
Не верно. Если вы поместите это в файл и передадите его вывод, ничего не появится.
majkinetor

OP не требовал подключения к файлу. И да, [System.Console]перенаправить в файл нельзя.
Slogmeister Extraordinaire

0
$host.UI.Write('Enabling feature XYZ.......')
Enable-SPFeature...
$host.UI.WriteLine('Done')

-1

желаемый o / p: Включение функции XYZ ...... Готово

вы можете использовать команду ниже

$ a = "Включение функции XYZ"

Запись-вывод "$ a ...... Готово"

вам нужно добавить переменную и оператор в кавычки. надеюсь, это будет полезно :)

Спасибо Techiegal


Запись-вывод предпочтительнее для вывода объектов в конвейер. Для отображения текста часто используется Write-Host, а в последнее время Write-Information используется для записи в информационный поток.
logicaldiagram

-3

Вы абсолютно можете это сделать. У Write-Output есть флаг под названием « NoEnumerate », который по сути то же самое.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.