今年早些时候,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 实用程序需要以下语法
date -v -1d
由于 PowerShell 在宽松许可下获得许可,并且专为所有平台构建,因此您可以将其与您的应用程序一起发布。 因此,当您的脚本在目标环境中运行时,它们将使用与您测试脚本的环境相同的命令实现,在同一个 shell 上运行。
对象和结构化数据
*nix 命令和实用程序依赖于您使用和操作非结构化数据的能力。 那些已经使用 `sed` `grep` 和 `awk` 生活多年的人可能不会对这种说法感到困扰,但有一种更好的方法。
让我们在 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 将很乐意通过其 Tab 键完成功能来帮助您浏览它。 只需键入 `$weather.` (确保包含“.”),然后按 Tab 键。 您将看到所有根级别的 JSON 键。 键入一个,后跟一个“` .`”,再次按 Tab 键,您将看到它的子级(如果有)。
因此,您可以轻松导航到所需的数据
> echo $weather.query.results.channel.atmosphere.pressure
1019.0
> echo $weather.query.results.channel.wind.chill
41
如果您有 JSON 或 CSV 格式的非结构化数据(或者由外部命令返回),只需将其传递到 `ConvertFrom-Json` 或 `ConvertFrom-CSV` cmdlet 中,您就可以在良好的干净对象中使用您的数据。
计算 vs. 自动化
我们使用 shell 有两个目的。 一种是用于计算,运行单个命令并手动响应其输出。 另一种是自动化,编写执行多个命令并以编程方式响应其输出的脚本。
我们大多数人已经学会忽视的一个问题是,这两个目的对 shell 提出了不同且相互冲突的要求。 计算要求 shell 简洁。 用户可以避免的击键越少越好。 用户键入的内容是否对另一个人来说几乎难以辨认并不重要。 另一方面,脚本是代码。 可读性和可维护性是关键。 在这里,POSIX 实用程序经常让我们失望。 虽然某些命令确实为其某些参数提供简洁和可读的语法(例如, `-f` 和 `--force`),但命令名称本身倾向于简洁而不是可读性。
PowerShell 包含几种机制来消除这种浮士德式的权衡。
首先,Tab 键完成功能消除了参数名称的键入。 例如,键入 `Get-Random -Mi`,按 Tab 键,PowerShell 将为您完成参数:`Get-Random -Minimum`。 但如果你真的想简洁,你甚至不需要按 Tab 键。 例如,PowerShell 会理解
Get-Random -Mi 1 -Ma 10
因为 `Mi` 和 `Ma` 各自都有唯一的完成。
您可能已经注意到,所有 PowerShell cmdlet 名称都具有动词-名词结构。 这可以帮助提高脚本的可读性,但您可能不想在命令行中一遍又一遍地键入 `Get-`。 所以不要这样做! 如果您键入没有动词的名词,PowerShell 将查找带有该名词的 `Get-` 命令。
注意:虽然 PowerShell 不区分大小写,但最好在您打算使用 PowerShell 命令时大写名词的第一个字母。 例如,键入 `date` 将调用系统的 `date` 实用程序。 键入 `Date` 将调用 PowerShell 的 `Get-Date` cmdlet。
如果这还不够,PowerShell 还有别名来创建简单的名称。 例如,如果您键入 `alias -name cd`,您会发现 PowerShell 中的 `cd` 命令本身就是 `Set-Location` 命令的别名。
因此,回顾一下 — 您可以获得强大的 Tab 键完成功能、别名和名词完成功能来保持您的命令名称简短、自动和*一致*的参数名称截断,同时仍然享受用于编写脚本的丰富、可读的语法。
所以... 朋友们?
这些只是 PowerShell 的一些优点。 还有更多我没有讨论的功能和 cmdlet(如果您想让 `grep` 哭泣,请查看 Where-Object 或其别名 `?`)。 嘿,如果您真的感到想家,PowerShell 会很乐意为您启动您的旧原生实用程序。 但给自己足够的时间来适应 PowerShell 的面向对象的 cmdlet 世界,您可能会发现自己选择忘记回来的路。
23 条评论