给大家安利一下 PowerShell

2019-03-18
5分钟阅读时长

代码更简洁、脚本更清晰、跨平台一致性等好处是让 Linux 和 OS X 用户喜爱 PowerShell 的原因。

今年(2018)早些时候,Powershell CoreMIT 开源协议发布了正式可用版(GA)。PowerShell 算不上是新技术。自 2006 年为 Windows 发布了第一版 PowerShell 以来,PowerShell 的创建者在结合了 Unⅸ shell 的强大和灵活的同时也在弥补他们所意识到的缺点,特别是从组合命令中获取值时所要进行的文本操作。

在发布了 5 个主要版本之后,PowerShell 已经可以在所有主流操作系统上(包括 OS X 和 Linux)本地运行同样创新的 shell 和命令行环境。一些人(应该说是大多数人)可能依旧在嘲弄这位诞生于 Windows 的闯入者的大胆和冒失:为那些远古以来(从千禧年开始算不算?)便存在着强大的 shell 环境的平台引荐自己。在本帖中,我希望可以将 PowerShell 的优势介绍给大家,甚至是那些经验老道的用户。

跨平台一致性

如果你计划将脚本从一个执行环境迁移到另一个平台时,你需要确保只使用了那些在两个平台下都起作用的命令和语法。比如在 GNU 系统中,你可以通过以下方式获取昨天的日期:

date --date="1 day ago"

在 BSD 系统中(比如 OS X),上述语法将没办法工作,因为 BSD 的 date 工具需要以下语法:

date -v -1d

因为 PowerShell 具有宽松的许可证,并且在所有的平台都有构建,所以你可以把 PowerShell 和你的应用一起打包。因此,当你的脚本运行在目标系统中时,它们会运行在一样的 shell 环境中,使用与你的测试环境中同样的命令实现。

对象和结构化数据

*nix 命令和工具依赖于你使用和操控非结构化数据的能力。对于那些长期活在 sedgrepawk 环境下的人们来说,这可能是小菜一碟,但现在有更好的选择。

让我们使用 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 属性 则用来获取该类型的全称。

那么,这种对象导向的 shell 是如何让你的工作变得更加简单呢?

首先,你可将任何对象排进 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 二进制对象,或者……

$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 数据(通过外部命令返回的),只需要将它相应的排进 ConverFrom-JsonConvertFrom-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

因为 MiMa 每一个都具有独立不同的补齐。

你可能已经留意到所有的 PowerShell cmdlet 名称具有动名词结构。这有助于脚本的可读性,但是你可能不想一而再、再而三的键入 Get-。所以并不需要!如果你之间键入了一个名词而没有动词的话,PowerShell 将查找带有该名词的 Get- 命令。

小心:尽管 PowerShell 不区分大小写,但在使用 PowerShell 命令是时,名词首字母大写是一个好习惯。比如,键入 date 将会调用系统中的 date 工具。键入 Date 将会调用 PowerShell 的 Get-Date cmdlet。

如果这还不够,PowerShell 还提供了别名,用来创建简单的名字。比如,如果键入 alias -name cd,你将会发现 cd 在 PowerShell 实际上时 Set-Location 命令的别名。

所以回顾以下 —— 你可以使用强大的 tab 补全、别名,和名词补全来保持命令名词简洁、自动化和一致性参数名截断,与此同时还可以享受丰富、可读的语法格式。

那么……你看呢?

这些只是 PowerShell 的一部分优势。还有更多特性和 cmdlet,我还没讨论(如果你想弄哭 grep 的话,可以查看 Where-Object 或其别称 ?)。如果你有点怀旧的话,PowerShell 可以为你加载原来的本地工具。但是给自己足够的时间来适应 PowerShell 面向对象 cmdlet 的世界,然后你将发现自己会选择忘记回去的路。


via: https://opensource.com/article/18/2/powershell-people

作者:Yev Bronshteyn 译者:sanfusu 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出