ChatGPT解决这个技术问题 Extra ChatGPT

如何在 PowerShell 中输出没有换行符的文本?

我希望我的 PowerShell 脚本打印如下内容:

Enabling feature XYZ......Done

脚本看起来像这样:

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

但是 Write-Output 总是在最后打印一个新行,所以我的输出不在一行上。有没有办法做到这一点?


J
Jay Bazuzi

Write-Host -NoNewline "Enabling feature XYZ......."


被否决是因为 OP 的示例专门使用了 Write-Output,它的功能与 Write-Host 大不相同。读者在复制/粘贴答案之前应注意这一巨大差异。
我同意@NathanAldenSr,如果您尝试输出到文件等,Write-Host 无济于事。
Write-Host 几乎从来都不是正确答案。这相当于在 Unixland 中执行 >/dev/tty
Write-Progress 在某些情况下可能是合适的,请参见下面的示例。
Write-HostWrite-Information 相同,可作为输出流 6 重定向。因此,您可以将其重定向到作为 6>output.txt 的文件。它没有“自己的思想”,只是不是你可以从 Unixland 带来的知识,仅此而已,所以很多人误解了它。
P
Peter Mortensen

不幸的是,正如在几个答案和评论中所指出的,Write-Host 可能很危险,不能通过管道传输到其他进程,并且 Write-Output 没有 -NoNewline 标志。

但是这些方法是显示进度的“*nix”方式,“PowerShell”的方式似乎是 Write-Progress:它在 PowerShell 窗口顶部显示一个带有进度信息的栏,从 PowerShell 3.0 开始可用, see manual 了解详情。

# 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"

我认为这是显示状态的最佳解决方案。如果您需要日志或其他内容,则必须使用 Write-Output 的换行符。
同意,加上渐进式显示的目的只是“花哨”进行实时安装,在日志文件中没有任何意义:打印“开始做某事”然后“完成某事”
s
shufler

虽然它可能不适用于您的情况(因为您正在向用户提供信息输出),但请创建一个可用于附加输出的字符串。当需要输出它时,只需输出字符串。

当然忽略这个例子在你的情况下很愚蠢,但在概念上很有用:

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

显示:

Enabling feature XYZ.......Done

这在提供的特定示例中可能有效,但 Write-Output 仍会产生额外的换行符。合理的解决方法,但不是解决方案。
Write-Output 总是在最后输出一个换行符。使用此 cmdlet 无法解决此问题
这不是重点,因为安装该功能后会出现整个输出。
我不明白,谁对这个问题给出了超过 1 个赞,因为这不是重点,因为整个输出出现在安装该功能之后
“当然忽略这个例子在你的情况下很愚蠢,但在概念上很有用:”
P
Peter Mortensen

要写入文件,您可以使用字节数组。以下示例创建一个空的 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)

这是一个很棒的例子!
P
Peter Mortensen

是的,因为其他答案有状态,它不能用 Write-Output 来完成。在 PowerShell 失败的地方,转向 .NET,这里甚至有几个 .NET 答案,但它们比它们需要的更复杂。

只需使用:

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

它不是最纯粹的 PowerShell,但它可以工作。


被否决,因为它的行为就像 Write-Host,除了人们不会期望它。
P
Peter Mortensen

在 PowerShell 中似乎没有办法做到这一点。以前的所有答案都不正确,因为它们的行为方式与 Write-Output 的行为方式不同,但更像 Write-Host,但无论如何都没有这个问题。

关闭解决方案似乎将 Write-Host-NoNewLine 参数一起使用。您不能通过管道处理这通常是一个问题,但有一种方法可以覆盖此函数,如 Write-Host => Export to a file 中所述,因此您可以轻松地使其接受输出文件的参数。这还远不是一个好的解决方案。使用 Start-Transcript 这更有用,但该 cmdlet 在本机应用程序中存在问题。

Write-Output在一般情况下根本无法满足您的需求。


P
Peter Mortensen

我遇到的问题是,在使用 PowerShell v2 时,Write-Output 实际上对输出进行了换行,至少到标准输出。我试图将 XML 文本写入标准输出但没有成功,因为它会在字符 80 处被硬包装。

解决方法是使用

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

这在 PowerShell v3 中不是问题。写输出似乎在那里正常工作。

根据调用 PowerShell 脚本的方式,您可能需要使用

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

在您写入标准输出之前。


表现得像 Write-Host,但最差。例如,您不能将其通过管道传输到文件中。
P
Peter Mortensen

shuffler 的答案是正确的。换一种说法:不是使用数组形式将值传递给 Write-Output,

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

这将防止由多次调用 Write-Output(或 ARRAY FORM)引起的中间 CRLF,但当然不会抑制 Write-Output commandlet 的最终 CRLF。为此,您必须编写自己的命令行开关,使用此处列出的其他复杂解决方法之一,或者等到 Microsoft 决定支持 Write-Output 的 -NoNewline 选项。

您希望向控制台提供文本进度表(即“....”)而不是写入日志文件,也应该通过使用 Write-Host 来满足。您可以通过将 msg 文本收集到一个变量中以写入日志并使用 Write-Host 向控制台提供进度来完成这两个任务。可以将此功能组合到您自己的命令行开关中,以实现最大程度的代码重用。


我更喜欢这个答案而不是其他答案。如果您要调用对象的属性,则不能将它们括在引号中,因此我使用了:Write-Output ($msg = $MyObject.property + "Some text I want to include" + $Object.property)
@Lewis您当然可以在字符串中包含对象属性!使用 $() 表达式包围任何变量,例如 "$($MyObject.Property) Some text I want to include $($Object.property)"
这可能在提供的特定示例中有效,但是 Write-Output 仍然产生了一个额外的换行符,您只是看不到它,因为它是最后写入的内容。合理的解决方法,但不是解决方案。可能有一些东西消耗了无法处理尾随换行符的结果输出。
不正确。该解决方案无法通过单个命令完成。
这并没有解决问题。日志文件输出应显示即将尝试的操作,以便可以查看和跟踪失败。串联并没有实现这一点。
d
durette

我作弊了,但我相信这是满足所有要求的唯一答案。也就是说,这避免了尾随的 CRLF,同时为其他操作提供了一个完成的地方,并在必要时正确地重定向到 stdout。

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

pwsh(powershell 核心)中,Add-Type 没有 -CompilerParameters 选项,但您可以对其他语言执行相同操作:python3 -c "import sys; print(sys.argv[1], end='')" "Enabling feature XYZ......."
f
famousgarkin

FrinkTheBrave 响应的简化:

[System.IO.File]::WriteAllText("c:\temp\myFile.txt", $myContent)

这根本不能回答问题。
但这正是我搜索的内容以及我对问题标题的期望。
c
chwarr

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

根据您对指示进度的需要,还有 Write-Progress
P
Peter Mortensen

您根本无法让 PowerShell 省略那些讨厌的换行符……没有脚本或 cmdlet 可以。当然,Write-Host 绝对是无稽之谈,因为您无法从中重定向/管道!

不过,您可以编写自己的 EXE 文件来执行此操作,这就是我在 Stack Overflow 问题 How to output something in PowerShell 中解释的操作。


信息不正确。正如 Shay 和 Jay 出色回答的那样,只需添加 -NoNewline 作为第一个参数。
也许现在@DavidatHotspotOffice就是这种情况,但是当我最后一次触摸不起作用的Windows框(一年多前)时,您无法从Write-Host重定向/管道。公平地说,我对 POSH 或 .NET 没有丝毫耐心,几个月后我退出并回到了 unix 领域。 funny
@DavidatHotspotOffice - 实际上,他是对的。 Write-Output 没有“NoNewLine”参数,这是原始问题所要问的。使用 Write-Output 似乎有一些很好的理由 - 所以这个答案是有道理的。 jsnover.com/blog/2013/12/07/write-host-considered-harmful
被否决是因为问题是在“在 PowerShell 中”寻求解决方案。编写外部 EXE 不是“在 PowerShell 中”。
@SlogmeisterExtraordinaire 这在 PowerShell 中是不可能的,因此我的回答是合理的。您之所以投反对票,是因为您非常难过,以至于您必须使用世界上最糟糕的操作系统,该操作系统具有世界上最糟糕的外壳。
B
BG100
$host.UI.Write('Enabling feature XYZ.......')
Enable-SPFeature...
$host.UI.WriteLine('Done')

不起作用:你不能管这个。
W
Will Martin

以下将光标放回上一行的开头。您可以将其放置在正确的水平位置(使用 $pos.X 将其横向移动):

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

您当前的输出超过 27 个空格,因此 $pos.X = 27 可能有效。


这与文件输出无关。
它也没有那么糟糕。如果您也执行 $pos.X,它可以产生正确的输出。问题是,如果将其通过管道传输到文件,则会出现两条单独的行。
S
Slogmeister Extraordinaire

它可能不是非常优雅,但它完全符合 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()

不正确。如果您将其放入文件并通过管道传输其输出,则不会出现任何内容。
管道到文件不是 OP 要求的。是的,[System.Console] 无法重定向到文件。
T
Tyler2P

使用内联连接的最简单方法——同时改为“写入输出”;例如 2 个制表符(字符串)& 然后是文字/逐字(字符串):

Write-Output ("`t`t" + '${devArg}')

D
Darrell

我无论如何都不是专家,但为什么不这样:


Write-Output "hello" | ForEach-Object {  $PSItem.Trim() } | Do-Whatever

这保持了流水线语义,但只是在将新行字符传递到流水线中之前修剪换行符,以供您接下来需要做的任何事情。如果那是写入文件,那就这样吧。如果这是写给主机,你可以这样做,但如果你确实写给主机,记得使用 | Write-Host -NoNewline

更新:根据我在下面的评论:“我明白为什么我的答案不起作用了。当管道传输到外部程序时,Powershell 不可避免地会附加一个新行字符作为管道语义的一部分。见:github.com/PowerShell/PowerShell/问题/5974 因此,当我将修剪后的文本传递到管道中时,新行字符将重新出现在输入中。”


好的,我明白为什么我的答案不起作用了。当管道传输到外部程序时,Powershell 不可避免地会附加一个新行字符作为管道语义的一部分。看到这个:github.com/PowerShell/PowerShell/issues/5974因此,当我将修剪后的文本传递到管道中时,新行字符将重新出现在输入中。
Z
Zachary Scott

link in the comments 中,该答案包括:

Write-Output "Some text $( $var.Name )"

这对我来说效果很好。如果您需要 ExpandProperty 来获取 Name 的单个值,则 $( ) 不是多余的,否则我的输出是这个而不是解析的值:

@{Name=Name; Address=Address; City=City}.Name

t
techiegal

所需的o / p:启用功能XYZ......完成

您可以使用以下命令

$a = "启用功能 XYZ"

写输出“$a......完成”

您必须在引号内添加变量和语句。希望这会有所帮助:)

感谢 Techiegal


Write-Output 优先用于将对象放入管道。对于文本显示,经常使用 Write-Host,最近使用 Write-Information 写入信息流。
S
Smelltastic

你绝对可以做到这一点。 Write-Output 有一个名为“NoEnumerate”的标志,它本质上是相同的。