Power(Shell) 还给人民

少输入代码,编写更简洁的脚本,跨平台一致运行,以及 Linux 和 OS X 用户可以爱上 PowerShell 的其他原因。
444 位读者喜欢这篇文章。
One lightbulb lit out of several

Opensource.com

今年早些时候,PowerShell Core 正式发布,并采用开源 (MIT) 许可证。PowerShell 并不是什么新技术。从 2006 年首次发布 Windows 版本开始,PowerShell 的创建者就 试图 融合 Unix shell 的强大功能和灵活性,同时弥补他们认为的不足,特别是需要进行文本操作才能从命令组合中获取价值。

经过五个主要版本的迭代,PowerShell Core 允许相同的创新 shell 和命令环境在所有主要操作系统(包括 OS X 和 Linux)上本地运行。有些人(读作:几乎所有人)可能仍然会嘲笑这个 Windows 出生的“入侵者”大胆地向自古以来(至少按照千禧一代的定义)就拥有强大 shell 环境的平台提供自身。在这篇文章中,我希望论证 PowerShell 甚至可以为经验丰富的用户提供优势。

跨平台的一致性

如果您计划将脚本从一个执行环境移植到另一个执行环境,则需要确保仅使用有效的命令和语法。 例如,在 GNU 系统上,您将按如下方式获取昨天的日期

date --date="1 day ago"

在 BSD 系统(例如 OS X)上,上述语法将不起作用,因为 BSD 日期实用程序需要以下语法

date -v -1d

由于 PowerShell 在宽松许可下获得许可,并且为所有平台构建,因此您可以将其与应用程序一起发布。 因此,当您的脚本在目标环境中运行时,它们将运行在与测试脚本的环境相同的 shell 上,使用相同的命令实现。

对象和结构化数据

*nix 命令和实用程序依赖于您使用和操作非结构化数据的能力。 那些已经使用 sed grepawk 多年的人可能不会对此声明感到困扰,但有一种更好的方法。

让我们在 PowerShell 中重做昨天的日期示例。 要获取当前日期,请运行 Get-Date cmdlet(发音为“commandlet”)

> Get-Date                         

Sunday, January 21, 2018 8:12:41 PM

您看到的输出实际上不是文本字符串。 相反,它是 .Net Core 对象的字符串表示形式。 就像任何其他 OOP 环境中的任何其他对象一样,它具有类型,并且通常具有您可以调用的方法。

让我们证明这一点

> $(Get-Date).GetType().FullName
System.DateTime

$(...) 语法的行为与您对 POSIX shell 的期望完全一样——括号中命令的求值结果将替换整个表达式。 然而,在 PowerShell 中,$ 在这样的表达式中是严格可选的。 而且,最重要的是,结果是一个 .Net 对象,而不是文本。 因此,我们可以在该对象上调用 GetType() 方法以获取其类型对象(类似于 Java 中的 Class 对象),以及 FullName 属性以获取类型的完整名称。

那么,这种面向对象的特性如何使您的生活更轻松?

首先,您可以将任何对象管道传输到 Get-Member cmdlet 以查看它必须提供的所有方法和属性。

> (Get-Date) | Get-Member
PS /home/yevster/Documents/ArticlesInProgress> $(Get-Date) | Get-Member         


   TypeName: System.DateTime

Name                 MemberType     Definition                                 
----                 ----------     ----------                                 
Add                  Method         datetime Add(timespan value)               
AddDays              Method         datetime AddDays(double value)             
AddHours             Method         datetime AddHours(double value)            
AddMilliseconds      Method         datetime AddMilliseconds(double value)     
AddMinutes           Method         datetime AddMinutes(double value)          
AddMonths            Method         datetime AddMonths(int months)             
AddSeconds           Method         datetime AddSeconds(double value)          
AddTicks             Method         datetime AddTicks(long value)              
AddYears             Method         datetime AddYears(int value)               
CompareTo            Method         int CompareTo(System.Object value), int ...

您可以快速看到 DateTime 对象有一个 AddDays,您可以快速使用它来获取昨天的日期

> (Get-Date).AddDays(-1) 

Saturday, January 20, 2018 8:24:42 PM

为了做一些稍微令人兴奋的事情,让我们调用 Yahoo 的天气服务(因为它不需要 API 令牌)并获取您当地的天气。

$city="Boston"
$state="MA"
$url="https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22${city}%2C%20${state}%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys" 

现在,我们可以使用老式的方法,只需运行 curl $url 即可获得一个巨大的 JSON Blob,或者...

$weather=(Invoke-RestMethod $url) 

如果您查看 $weather 的类型(通过运行 echo $weather.GetType().FullName),您会看到它是一个 PSCustomObject。 这是一个动态对象,反映了 JSON 的结构。

PowerShell 将很高兴通过其制表符补全功能来帮助您浏览它。 只需键入 $weather. (确保包含“.”)并按 Tab 键。 您将看到所有根级别的 JSON 键。 键入一个键,后跟一个 ".",再次按 Tab 键,您将看到它的子项(如果有)。

因此,您可以轻松地导航到所需的数据

> echo $weather.query.results.channel.atmosphere.pressure                                                              
1019.0

> echo $weather.query.results.channel.wind.chill                                                                       
41

如果您有 JSON 或 CSV 文件(或由外部命令返回)作为非结构化数据,只需将其管道传输到 ConvertFrom-JsonConvertFrom-CSV cmdlet 中,您就可以将数据放在漂亮干净的对象中。

计算 vs. 自动化

我们使用 shell 有两个目的。 一种用于计算,运行单个命令并手动响应其输出。 另一种用于自动化,编写执行多个命令并以编程方式响应其输出的脚本。

我们大多数人已经学会忽略的一个问题是,这两个目的对 shell 提出了不同且相互冲突的要求。 计算要求 shell 简洁。 用户可以输入的击键越少越好。 用户键入的内容是否对另一个人来说难以辨认并不重要。 另一方面,脚本是代码。 可读性和可维护性是关键。 在这里,POSIX 实用程序常常让我们失望。 虽然某些命令确实为其某些参数提供简洁和可读的语法(例如,-f--force),但命令名称本身在简洁性方面犯了错误,而不是可读性。

PowerShell 包含多种机制来消除这种浮士德式的权衡。

首先,制表符补全消除了参数名称的输入。 例如,键入 Get-Random -Mi,按 Tab 键,PowerShell 将为您完成参数:Get-Random -Minimum。 但如果您真的想简洁,甚至不需要按 Tab 键。 例如,PowerShell 会理解

Get-Random -Mi 1 -Ma 10

因为 MiMa 各自具有唯一的补全。

您可能已经注意到所有 PowerShell cmdlet 名称都具有动词-名词结构。 这有助于脚本的可读性,但您可能不想在命令行中一遍又一遍地输入 Get-。 所以不要! 如果您键入一个没有动词的名词,PowerShell 将查找带有该名词的 Get- 命令。

注意:虽然 PowerShell 不区分大小写,但最好在打算使用 PowerShell 命令时将名词的第一个字母大写。 例如,键入 date 将调用系统的 date 实用程序。 键入 Date 将调用 PowerShell 的 Get-Date cmdlet。

如果这还不够,PowerShell 还有别名来创建简单的名称。 例如,如果您键入 alias -name cd,您会发现 PowerShell 中的 cd 命令本身是 Set-Location 命令的别名。

因此,回顾一下——您可以获得强大的制表符补全、别名和名词补全,以保持命令名称简短、自动和一致的参数名称截断,同时仍然享受用于脚本编写的丰富、可读的语法。

所以... 朋友?

这些只是 PowerShell 的一些优势。 还有更多我没有讨论的功能和 cmdlet(如果您想让 grep 哭泣,请查看 Where-Object 或其别名 ?)。 嘿,如果您真的感到思乡,PowerShell 将很乐意为您启动旧的本机实用程序。 但给自己足够的时间来适应 PowerShell 的面向对象的 cmdlet 世界,您可能会发现自己选择忘记回头的路。

User profile image.
软件工程师,具有开源治理倾向。 偶尔的开发者宣传者。 所有意见仅代表我个人。 脑海中的想法在这里。

23 条评论

很棒的文章! 在我担任系统管理员的日子里,如果我有一种比文本字符串更丰富的数据方法,那么脚本编写会容易得多(尽管文本字符串也有一些优点)。 但是,由于大多数 *nix 命令只或主要对文本字符串进行操作,那么与 Python、Ruby 等相比,使用 PowerShell 有什么优势呢?

好问题。 在选择一个工具而不是另一个工具时,存在一定的主观性。 对我而言,PowerShell 的优势在于它作为命令 shell 和脚本语言的特定、有意的二元性。 例如,在 Ruby 中删除文件比在 PowerShell 中需要更多的输入。 并且制表符补全和统一的命令名称风格也不会存在。

我认为将 PowerShell 看作一种用于自动化的 DSL 而不是一种编程语言会很有帮助。 您不会使用 PowerShell 来编写 Web 应用程序。 您可以使用 PowerShell 来自动配置系统上的 Web 应用程序或将 Web 应用程序部署到公共云(使用供应商提供的模块)。

这有帮助吗?

回复 ,作者是 bcotton

这太荒谬了。 Powershell 不一致 - 有些 cmdlet 会正确地进行 try/catch,有些则不会(即:返回异常)。
您最好使用 Python 之类的东西....

我没有遇到您描述的不一致性。 您能举一些例子吗?

回复 ,作者是 joeb(未验证)

不用了,谢谢。 我不知道还能说什么才能保持礼貌。

厌倦了 Microsoft 和 Windows 试图“模仿”开源方式!……哈哈! 似乎他们终于撞上了“墙”,并意识到他们做事的方式只能让他们走到这一步,现在他们想成为开源的“全新创新者”? 不,他们有机会与 Unix 和 Linux 友好相处,但却搞砸了。 以至于我们在开源世界中拥有一切我们需要的,从媒体播放器到 Web 浏览器,再到办公套件、PDF 阅读器、BASH 脚本和命令行界面等等。 甚至没有理由试图将两者混合在一起。 让那些想尝试这样做的人继续这样做吧,但我对我的 Linux 命令行语法和功能非常满意。 对不起。 但并非所有人都想将 Windows 与他们首选的操作系统“融合”。

我是微软的员工(仅代表我个人观点,不代表公司),而且我已经有超过十年没有在自己的机器上运行Windows了。我对我的Linux设置非常满意。您说得对,微软已经意识到它对开源的态度是错误的。但说混合使用这两者没有真正的理由,完全无视了许多人确实运行混合环境的事实(无论是出于选择还是雇主的强制要求)。能够跨多个平台使用相同的工具对系统管理员来说非常有价值。您说得对,不是每个人都想将Windows与他们首选的操作系统混合使用。但显然有些人确实想这样做。

回复,由访客 (未验证) 撰写

哦,我完全同意你的观点。很可能 *有* 人想同时运行它们,但对我来说?我已经决定不使用Windows了。而且要理解...我自99年以来一直在I.T.领域工作,并且自3.1以来一直在使用Windows。所以我了解它的来龙去脉。虽然大众可能会被它的“新”所迷惑,但它并不是。它仍然是与Windows 95相同的旧Windows... Windows 95是Windows 98的前身.... Windows 98是Windows NT 4.0的前身,后者导致了Windows 2000.....然后是2003......2007.....Vista.....XP......Win7....Win8...以及最近的Win10。所以我知道,虽然事情可能被称为不同的名字,并且事情可能得到新的图标和系统中的新位置,但它仍然是一个严重依赖“.exe”的系统,而“.exe”可以被各种各样的有害的东西所浸染!即使PowerShell摆脱了运行Windows的大部分代码,它仍然“附加”到它上面,对我自己来说,这不是我想尝试的东西。此外,我从2001/02年就开始使用Linux,并且已经学会了如何使用Linux命令在终端中移动,我为什么要“学习”微软的语法?对于需要它的系统管理员来说...我说去做吧...但我永远不需要Windows了...所以我选择消除来自该平台的任何东西干扰或影响我的Linux服务器和数据库的可能性。但我确实看到了你的观点,Windows管理员/开发人员可能需要在Linux机器上运行它,以查看它是否/如何针对他们的代码运行,或者查看从Linux运行某些东西的结果以及它如何移植到Windows。我只是不需要它,对我来说...整个“微软热爱Linux”的说法并不成立,我知道还有关于专利的诉讼案件可能在未来十年或更长时间内持续进行...所以不。不,谢谢...我不需要Windows。

回复,由bcotton 撰写

Edward,

PowerShell 6构建在.Net Core之上,这是一个新的.Net模块化运行时,它为每个目标平台本地构建。没有exe文件。没有模拟器。没有Windows。

回复,由Edward G OConnor (未验证) 撰写

虽然不是程序员,但我必须学习Shell脚本才能成功且胜任地完成许多关键的脚本任务。

在很多情况下,Powershell的功能根本无法与BASH相提并论。以简单的ls命令为例,它至少有8-10个参数,用于查看/列出Powershell缺少的信息。

最后,如果Powershell如此“强大”,那么为什么微软将完整的Ubuntu BASH Shell脚本工具集集成到Windows 10中,以及ISO Python、openSSH和几乎所有其他UNIX/Linux工具?

没有工具适用于所有用例。如果Bash如此强大,为什么Fedora在其存储库中包含ZSH?

回复,由W. Anderson (未验证) 撰写

PowerShell确实包含“dir”cmdlet (Get-ChildItem的别名),它起着“ls”的作用。最初的Windows版本的PowerShell实际上也将其别名为“ls”。我假设删除该别名是为了避免与Posix平台上的本机“ls”命令冲突。

您可以在这里阅读Get-ChildItem (又名“dir”)如何处理文件: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell…

使用PowerShell,您不需要那么多参数来指定要返回的信息,因为您得到的是.Net对象,您可以使用Where-Object (又名“where”,又名“?”) cmdlet查询它们的属性。

不要假设解决方案不存在,而只是因为它没有立即显现出来。

回复,由W. Anderson (未验证) 撰写

> 因为PowerShell是根据宽松的许可证授权的,并且是为所有平台构建的,所以您可以将其与您的应用程序一起发布。

您也可以发布任何其他FLOSS许可的语言。使用真正的编程语言而不是bash的原因是错误处理,而不是易于操作。在power shell中给我展示一个写得好的错误处理代码是什么样子的,我可能会考虑它。否则没有理由切换到另一个混合的东西,它假设开源意味着每个人都应该切换到它。

顺便说一句,也有python shell。可能还有一大堆其他*修复*问题的shell计划。特别是当我读到大小写不敏感、尝试将命令与某些前缀匹配、猜测部分命令参数和其他*有帮助*的功能时……这使得静态分析变得不可能,降低了代码的可读性并且容易出错。而且这些部分参数匹配绝对不是面向未来的,因为添加新参数很容易破坏现有的假设。

Alexander,

这是一个教程,描述了PowerShell的所有异常处理功能。 https://www.vexasoft.com/blogs/powershell/7255220-powershell-tutorial-t…. 这篇文章显然是在Windows上编写的,因为它使用了Windows路径,但所有错误处理机制在每个平台上都以相同的方式工作。

我并不是建议您使用PowerShell来替代Python编写应用程序代码。我建议您在使用PowerShell编写bash shell脚本之前使用它。即使您选择使用实际的编程语言来实现实现逻辑,使用PowerShell编写包装器脚本而不是Posix shell可以通过我在帖子中描述的功能提供更流畅的用户体验。

另请记住,PowerShell在.Net Core平台上运行,并与.Net类型系统进行本地交互。因此,如果您愿意,您可以使用C#、F#或任何其他构建符合.Net Standard的库的语言来实现实际的应用程序逻辑,然后通过PowerShell调用该逻辑,并接收结构化对象作为输出,以便于后续操作。

回复,由Aleksandar Kos… 撰写

PowerShell让我能够轻松地修改目录树中所有文件夹的“修改日期”(这样我可以按日期查看我的项目文件夹,以了解我上次处理每个项目的时间)。我最终可以用PowerShell中的一行代码完成此操作。这是因为recurse以及管道和过滤返回对象的能力。这是一台Windows PC,所以我不确定是否有其他方法可以在其他系统上执行相同的操作。

该脚本深入每个文件夹,并将每个文件夹的修改日期设置为该文件夹中包含的任何文件或文件夹的最新修改日期。我在单行版本中硬编码了路径。一个更长的版本提示你输入目标文件夹并提供一个进度条(并在PowerShell IDE中报告错误)。

Get-ChildItem "H:\DropBox\FolderDateFix\Test1" -recurse | Where-Object {$_.PsIsContainer} | ForEach-Object {$_.LastWriteTime = ($_ | Get-ChildItem -recurse | Where-Object {!$_.PsIsContainer} | Sort-Object LastWriteTime | Select-Object -last 1).LastWriteTime}

如果您想了解每个对象的作用的更多细节,我会在一篇博客文章中分解并逐行浏览。

http://fettricks.blogspot.com/2014/08/useful-folder-modification-dates-…

这是一个很好的例子,说明了powershell可以轻松完成的事情!我认为Get-ChildItem、Select-Object和Where-Object是为数不多的别名比全名更直接的cmdlet之一。所以我将分别用“cd”、“select”和“?” (或“where”) 替换它们。

您是否尝试使用Measure-Object -Maximum来获取最新的文件修改日期,而不是对整个目录进行排序?

回复,由Shane Trent (未验证) 撰写

我已经做了超过10年的Windows管理员。长期以来,我一直抵制使用Powershell。而是使用了Python。例如,通过使用Python的ctypes调用Win32 API。

主要是因为我发现Powershell与Windows安装(以及Windows附带的某些框架)不透明地链接在一起。Powershell曾经是Windows的一个“不可删除”的部分。每个Windows都附带一个不同的Powershell版本,这使得脚本高度不兼容且难以针对这些各种目标进行测试(如果我在Windows 10上使用最新的Powershell版本来开发一个足够复杂的脚本,我非常肯定我的脚本将无法在原始的Windows 7上运行)。更新Powershell意味着您必须安装Windows更新(其行为与常规MSI安装程序不同 - 在许多方面更难在大量计算机上部署 - 即,它需要重新启动等)。此更新是一个可选更新 - 通常在大公司的WSUS服务器上从自动批准中排除。我见过不止一个企业环境,其中每台计算机都安装了不同的Powershell版本。

使用Python,事情要容易得多(我可以将Python安装放在网络共享上或将其与我的脚本捆绑在一起)。

快进到2018年初

- Powershell是开源的
- Powershell已与操作所需操作的各种框架分离
- 最重要的框架 .NET 不再是先决条件,但所需的 .NET Core 运行时已与 Powershell 捆绑在一起。
- .NET Core 本身已从 Windows 中“分离”,并成为跨平台的开源项目。
- 这使得 Powershell 安装是独立/便携式的(只需将 zip 文件解压缩到任何地方并运行 pwsh.exe)。
- pwsh 仍然需要操作系统提供的一些外部框架。对于 Windows 管理员来说,最重要的一个是 WMI / CIM。 虽然我现在可以将 Powershell 与我的脚本捆绑在一起,但恼人的是,“get-wmiobject / get-cimobject” 只有在本地 Windows 系统上安装了 Windows Management framework 5+ 时才能工作(该框架也提供 Powershell 5.1)。 然而,现在至少可以清楚地知道哪些框架实际上是 Windows 框架,而不是 Powershell+WMI+其他东西膨胀的产物。
- Powershell 6 中已经消失了一些有用的东西,因为它使用了仅限 Windows 的框架(例如 out-gridview)。 这是为跨平台一致性做出的牺牲(没有仅限 Windows 的 cmdlet)。
- 通过开源实现的透明度:Powershell 6 是 github 上一个维护良好的项目。 维护人员非常友好,热衷于回答问题等等。 从未见过微软提供如此出色的公共/免费支持。

总的来说,这解决了我在使用 Powershell 时的大部分痛点。 最大的痛点仍然是“get-wmiobject”/“get-cimobject”,它们对于大多数 Windows 管理任务都非常重要,并在大多数脚本中使用,但仍然依赖于安装最新的 Windows Management Framework。

Powershell 比 Python 真正优秀的地方在于它能够利用外部 C 和 .NET 库。 使用 .NET 程序集相对容易,而且是原生支持的。(记住:Powershell 是一个 .NET 应用程序,带有一个 .NET JIT 编译器。 你的 Powershell 代码会被动态编译成 .NET 字节码)。 调用“经典”的 Win32 API (C 库) 稍微复杂一些,但仍然比在 Python 中为 ctype 创建结构容易得多。

很高兴看到 Powershell 自 5 / 5.1 版本以来的发展方向。 它比以往任何时候都更不像一个 shell,而更像我更愿意称之为 “.NET 脚本”的东西。
- 首先,Powershell 语言现在能够支持类/OOP。
- 其次,Powershell 现在能够运行和执行 "C#" 代码(无需将其编译为 .NET 字节码文件 - 它内置了一个 .NET JIT 编译器)。

在使用 *nix shell 多年之后,我现在正在学习 PowerShell。 尽管新的开源方法正在推动它,但我对文章的最初前提存在疑问 -- (1) 如果你使用特定的 shell(例如,BASH,或者如果你很勇敢,可以使用 SH),你确实可以在各个平台上保持一致性 (2) 另一方面,阅读文章中引用的一篇参考文献,它接着宣布某些功能在 4.0 版本中不再受支持。 所谓的“随时随地相同”呢?

Bash/sh 可能会在各个平台上提供一致性,但它们调用的实用程序有时不会。

我并不是断言 powershell 从不弃用或修改功能,或者所有语法在各个版本中都相同。 但是,你可以通过简单地将正确版本的 Powershell(6.0 或更高版本)与你的脚本捆绑在一起,来确保在执行中获得相同的功能和语法。 换句话说,你的应用程序可以拥有自己的自动化环境,而不是依赖于操作系统来提供它。 这就是你如何保证一致性的方法。

回复 作者:Erez L. (未验证)

尽管微软可能会投资从自己的存储库中提供 .deb 和 .rpm 用于 powershell 安装,但只有当它可以从 DEBIAN/REDHAT/CENTOS 的标准/主要存储库本身获得时,组织和人们才会认真地在更多严肃的项目中使用它。
例如:(非常重要)
请参阅 Debian 维护者对将 powershell 包含在 Debian 主要存储库中的一些担忧
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=834756#16

在上述问题得到解决并包含在主要存储库中之前,它不会确保/证明最终用户 powershell 编译的所有先决条件都以开源兼容的方式满足,并且在最后一个构建链中没有任何专利问题。

恕我直言,信任微软是对历史的直接一击。 这家公司现在“对开源友好”仅仅是因为微软时代岌岌可危。 最好信任其他所有在自由软件不确定且主要是一种道德哲学时就采取主动的人,对他们来说,*人* 比 *钱* 更重要。

Creative Commons License本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
© . All rights reserved.