如何将一个字符串同时输出到变量和控制台

12

在 PowerShell 中,是否有一种简单的方法可以同时将字符串输出到变量和控制台?

我想捕获脚本的输出并保存到变量中,以便在脚本结束时分析它、将其保存到日志文件并通过电子邮件发送给操作员。

我的意图是创建一个名为 $output 的变量,并将任何输出字符串添加到其中,同时立即输出到控制台,类似于:

$output="Process started"

$output=+"Doing step 1"
"Doing step 1"

$output=+"Doing step 2"
"Doing step 2"

最终,我可以将$output保存到日志文件中、通过电子邮件发送并解析它。

我尝试使用tee-object来实现这个目的,但不幸的是,它会重写我的$output变量,而不是将字符串追加到它后面。

更新 这是我决定采用的最终解决方案-感谢manojlds!

$script:output = ""

filter mylog {
    $script:output+= $_+"`n"
    return $_
}


"doing step {0}" -f 1 | mylog
"doing step {0}" -f 2 | mylog
"doing step {0}" -f 3 | mylog

#in the end of the script
$script:output
7个回答

6

有很多方法可以实现您的最终目标:

在您的脚本中,只需添加以下内容:

"Process started"
<step1>
"Doing step 1"
<step2>
"Doing step 2"
...

然后按以下方式运行脚本:

.\test.ps1 | Tee-Object -file log.txt

请注意,输出可供Tee-Object使用,并在发生时出现在控制台上。您不必等到整个脚本运行后才能获得输出。这就是 Powershell 中流水线的工作原理。对象会随着它们的发生而向下游传递。在步骤之间插入 sleep 10,看看输出是否在可用时立即出现。
此外,您不一定需要另一个脚本(launcher.ps1)来实现您所说的功能。您可以在脚本内部使用函数、脚本块等。
其他一些方法:
function test {

"Process started"
sleep 5
"Doing step 1"
sleep 5
"Doing step 2"

}

test | %{$output+=$_;$_}
#use output
write-host -fore blue $output

您可以创建一个过滤器:
$script:output = ""

filter appendOutput {

    $script:output+= $_
    return $_
}

"Process started" | appendOutput
sleep 5
"Doing step 1" | appendOutput
sleep 5
"Doing step 2" | appendOutput
#use output
write-host -fore blue $script:output

这可能有更多的做法。

这是我最初的实现方式,但最终得到了两个脚本。现在我有一个更复杂的脚本,需要在调用脚本中进行一些分析,这使得我的调用脚本变得复杂。我刚刚发现了这篇文章http://get-powershell.com/post/2008/06/26/Stuffing-the-output-of-the-last-command-into-an-automatic-variable.aspx,现在正在考虑是否可以像覆盖out-default那样做些事情。 - mishkin
@mishking - .\test.ps1 | Tee-Object -file log.txt - 如果你这样做,你会在控制台上看到输出的实时情况。在上述脚本字符串之间添加 sleep 10 并查看。同时请查看我的更新答案。 - manojlds
谢谢您的帮助!日志文件在我的脚本中是可选的,所以我想先将输出收集到一个变量中,然后根据传递给脚本的参数,在脚本的结尾将其保存到日志文件和/或通过电子邮件发送。在启动器脚本中,我没有使用点号源,只是在$output变量中捕获了主脚本的输出-我想这就是为什么我没有实时看到控制台输出的原因。 - mishkin
如果我总是需要输出日志文件,那么你提出的方法非常好,但如果像我的情况一样,这是一个可选项怎么办?我也想只有一个脚本,并在其末尾设置一个变量,以便我可以解析一些关键字并实时在控制台输出。所有这些都可能吗? - mishkin
@mishkin - 我已经更新了我的回答。此外,.\test.ps1 技术上只是运行脚本,而不是点源,即 . test.ps1 是不同的。让我为你想要的更新我的回答。 - manojlds
显示剩余2条评论

6

一个不错的技巧,将你的变量赋值括在括号里。在PowerShell语言规范中(第7.1.1节分组括号),可以找到更多相关内容。可在此下载 here 或在线查看 here

PS > ($var=1)
1
PS >

太棒了!这是一个非常酷的技巧,感谢那个规范 - 里面有大量的好信息。不过在我的情况下,这个技巧帮助不大,因为如果我需要在一个变量中累积输出,就像这样: $output="" ($output+="doing step {0}" -f 1) ($output+="doing step {0}" -f 2)我会得到:doing step 1 doing step 1doing step 2我现在会像manojlds建议的那样坚持使用过滤器。 - mishkin

1

我对PowerShell的了解还不够,无法给出具体的答案。但如果用C语言来做的话,我会利用副作用。

//Psuedo-C
string con (oldString, newString) {
  print(newString);
  return oldString + newString;
}

使用函数的方式如下:
myString = con(myString, "Process started");

这样会有预期的效果。(不考虑正确的 C 语法和繁琐的事项,如处理换行符)我不知道如何将其翻译成 PowerShell。

然而,你想要做的可能被认为是混乱的。如果你在代码中明确输出并记录一遍又一遍的输出内容和日志,可能会更清晰明了。副作用最终会影响你。保持模块化。


1

我之前看过类似的东西,不同的是我不需要之后进行分析,只需要收集输出结果。

有人可能会对此感兴趣,因为你已经得到了答案,可以使用PowerShell记录(Start-Transcript和Stop-Transcript)。我从site上发现当你遇到服务器错误时,它确实存在一些问题,但他展示了如何处理这些问题。


谢谢!可能对某些人有效,我也研究过这个,但它有一些限制,就像这里描述的那样:http://blogs.msdn.com/b/powershell/archive/2010/01/04/workaround-for-start-transcript-on-native-processes.aspx - mishkin

0
稍微解释一下@manojlds的答案,您可以简单地使用Tee-Object cmdlet,但是不必为其指定要写入的文件,而是可以直接告诉它将内容写入变量中,如下所示:
.\test.ps1 | Tee-Object -Variable output

脚本/cmdlet/函数等的所有输出将实时写入控制台屏幕,并在脚本运行结束时存储在$output变量中。因此,您可以使用该变量分析或将其写入文件。这避免了您在操作完成后将文件内容读入变量的步骤。

0

脚本将在输出屏幕上写入数据,并将同样的数据存储在变量中

get-process | Tee-Object -Variable test

$test can use further 

-1
("Process started" | out-host) | Set-Variable x ; $x

谢谢,但这不起作用 - $x 每次都会被重置,而我需要它来累积整个脚本的输出。 - mishkin
-1. 这根本不起作用! $x 将是空的,因为 out-host 已经获取了字符串,你将得到该字符串的输出来设置变量,但实际上什么也没有设定(写入主机并不意味着它可以通过管道使用)。 - manojlds

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