如何在PowerShell中输出内容

253
我正在批处理文件中运行PowerShell脚本。该脚本获取一个网页并检查页面内容是否为字符串"OK"。
PowerShell脚本向批处理脚本返回错误级别。
批处理脚本由FTP自动化程序ScriptFTP执行。如果发生错误,我可以让ScriptFTP将完整的控制台输出通过电子邮件发送给管理员。
在PowerShell脚本中,如果网站的返回值不是"OK",我想输出该返回值,以便将错误消息包含在控制台输出中,并因此包含在状态邮件中。
我对PowerShell很陌生,不确定应该使用哪个输出函数来实现此目的。我可以看到三个:
- Write-Host - Write-Output - Write-Error 应该使用哪一个来写入Windows等效的stdout
7个回答

235

在PowerShell中,简单地输出内容是一件非常优美的事情 - 这也是它最大的优势之一。例如,常见的“Hello, World!”应用程序只需一行代码即可完成:

"Hello, World!"

它创建了一个字符串对象,赋予了上述的值,并且作为命令管道的最后一项调用.toString()方法并将结果输出到STDOUT(默认情况下)。 美妙无比。

其他的Write-*命令是特定于将文本输出到它们关联的流中,并在这方面有其合适的位置。


12
这并不是真正发生的事情。这是一个字符串文字表达式,是其管道中唯一的内容。因此,它相当于"Hello, World!" | Out-Host。另一方面,Out-Host将对象发送到PowerShell主机以供显示,并且其实现取决于主机。控制台主机将它们发送到标准输出处理程序(通过Out-Default),确实如此。然而,PowerShell ISE在其输出窗格中显示它们,其他主机可能完全做其他事情。 - Joey
6
对象也不会被盲目地转换为字符串,而是通过一个格式化器进行处理,该格式化器决定如何处理它们。这就是为什么你会看到以表格形式呈现Get-ChildItem的输出,例如具有许多属性(例如WMI)的结果默认使用Format-List - Joey

143
我认为在这种情况下,你需要使用Write-Output。 如果你有一个类似于以下脚本的脚本:
Write-Output "test1";
Write-Host "test2";
"test3";

如果您使用重定向输出调用脚本,例如yourscript.ps1 > out.txt,则屏幕上将显示test2,而在 "out.txt" 中将显示test1\ntest3\n

请注意,“test3”和Write-Output命令始终会将新行附加到文本末尾,在PowerShell中没有办法停止这一点(即,使用本机命令在PowerShell中执行echo -n是不可能的)。如果您想要(在Bash中相对基本和简单的)echo -n功能,请参见samthebest's answer

如果批处理文件运行PowerShell命令,则很可能会捕获Write-Output命令。我已经与系统管理员进行了“长时间的讨论”,关于应该写入控制台的信息和不应该写入控制台的信息。我们现在同意,唯一需要使用Write-Host记录的信息是脚本是否成功执行或失败,所有其他脚本作者可能需要了解的执行信息(更新了哪些项目,设置了哪些字段等)都需要使用Write-Output记录。这样,当您向系统管理员提交脚本时,他可以轻松地使用runthescript.ps1 >someredirectedoutput.txt运行该脚本并查看屏幕上的结果是否正常。然后将“someredirectedoutput.txt”发送回开发人员。


18

我认为以下是展示 Echo vs. Write-Host 的良好例子。注意,test() 实际上返回一个整数数组,而不是像人们可能会误以为的单个整数。

function test {
    Write-Host 123
    echo 456 # AKA 'Write-Output'
    return 789
}

$x = test

Write-Host "x of type '$($x.GetType().name)' = $x"

Write-Host "`$x[0] = $($x[0])"
Write-Host "`$x[1] = $($x[1])"

上述的终端输出:

123
x of type 'Object[]' = 456 789
$x[0] = 456
$x[1] = 789

1
我可以整天看这个...但为什么 $x = test 只打印了 123 而没有打印 456 - not2qubit
Write-Output(或echo)将指定的对象写入管道。如果Write-Output是管道中的最后一个命令,则这些对象将显示在控制台中。在此示例中,它是倒数第二个命令,并被传递到return语句并添加到数组中。 - Tony

8
您可以在您的场景中使用任何一个,因为它们都写入默认流(输出和错误)。如果您将输出导向另一个命令,则需要使用Write-Output,这最终会终止于Write-Host
本文介绍了不同的输出选项:PowerShell O is for Output

1
谢谢提供链接。天哪,这个太复杂了。如果Write-host是某种终点或刷新函数,我会试着使用它并回报结果。 - Pekka

8

如何在Windows中写入等效于stdout的内容?

不幸的是,截至v7.2,无论是Windows PowerShell还是PowerShell Core,当通过PowerShell的CLI从外部调用时,它们会将其6个(!) 输出流全部发送到stdout

  • 请参阅GitHub问题#7989以讨论此有问题的行为,这可能不会被修复,以保持向后兼容性。

  • 实际上,这意味着无论您将PowerShell流发送到哪里,外部调用者都将将其视为stdout输出:

    • 例如,如果您从cmd.exe运行以下命令,您将看不到任何输出,因为对NUL的stdout重定向同样适用于所有PowerShell流:

      • C:\>powershell -noprofile -command "Write-Error error!" >NUL
    • 然而-奇怪的是-如果您重定向stderr,PowerShell 将其错误流发送到stderr,因此使用2>可以有选择地捕获错误流输出;以下仅输出'hi'-成功流输出-同时在文件err.txt中捕获错误流输出:

      • C:\>powershell -noprofile -command "'hi'; Write-Error error!" 2>err.txt

期望的行为是:

  • 将 PowerShell 的成功输出流(数字1)发送到 stdout

  • 所有其他流的输出发送到 stderr,这是唯一的选项,因为在进程之间只存在两个输出流 - 用于数据的 stdout(标准输出)和用于错误消息以及所有其他类型的消息(例如状态信息)的 stderr(标准错误)。

  • 尽管目前没有被尊重,但在代码中进行此区分是可取的


PowerShell内部

  • Write-Host 用于展示输出,绕过成功输出流,因此它的输出既不能直接被捕获到变量中,也不能被抑制或重定向。

    • 它最初的目的仅是创建用户反馈并创建简单的基于控制台的用户界面(彩色输出)。

    • 由于先前无法被捕获或重定向,PowerShell版本5使Write-Host写入新引入的信息流(编号6),因此自那时以来,可以捕获和重定向Write-Host输出。

  • Write-Error 用于将非终止性错误写入错误流(编号2);从概念上讲,错误流相当于stderr。

  • Write-Output 写入成功[输出]流(编号1),从概念上讲等同于stdout;它是写入数据(结果的流。

    • 然而,由于PowerShell的隐式输出功能很少需要显式使用Write-Output
      • 来自任何未被显式捕获、抑制或重定向的命令或表达式的输出会自动发送到成功流;例如,Write-Output "Honey, I'm $HOME""Honey, I'm $HOME"是等效的,后者不仅更简洁,而且更快。
      • 有关更多信息,请参见this answer

4
Write-Host "Found file - " + $File.FullName -ForegroundColor Magenta

Magenta可以是“System.ConsoleColor”枚举器的值之一 - Black,DarkBlue,DarkGreen,DarkCyan,DarkRed,DarkMagenta,DarkYellow,Gray,DarkGray,Blue,Green,Cyan,Red,Magenta,Yellow,White。

+ $File.FullName是可选的,并显示如何将变量放入字符串中。


2

你无法让PowerShell省略那些讨人厌的换行符。没有任何脚本或cmdlet可以做到这一点。

当然,Write-Host是绝对无用的,因为你不能从它进行重定向/管道操作!你只能编写自己的代码:

using System;

namespace WriteToStdout
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args != null)
            {
                Console.Write(string.Join(" ", args));
            }
        }
    }
}

例如。
PS C:\> writetostdout finally I can write to stdout like echo -n
finally I can write to stdout like echo -nPS C:\>

你可以在PowerShell本身中完成这个操作。[System.Console] :: Write(“stringy”) 但是在PowerShell中,你将无法重定向它。 - Nicholi
2
当然可以。请参考这个问题 - Slogmeister Extraordinaire

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接