一直以来使用ls,find,grep,sed等在linux下完成日常的系统配置,查看工作。自然而然的就认为世界就是这个样子,直到有一天我尝试了一下powershell。类似的情况也发生在我尝试使用函数式语言clojure的时候。 从出身时间来看,Powershell比unix的工具集要年轻许多。年代久远就意味这被时间证明的,可靠的,被广泛接受的,这么说诚然没错。但从另一个角度来看,也可以说是陈旧的,过时的,背满包袱的。这篇关于powershell的文章不是用来吹嘘或者布道Powershell,目的是给读者另外一个视角,消除一些技术之外的偏见。因为我常常看到一些程序员对于某家公司的产品毫无理由的贬低。 以下是从一个linux程序员的视角,觉得蛮不错的Powershell的特性。
统一规范,特别是cmd-let的命名和参数规则
其实我的英语水平中偏上,但对于linux的一些命令名称还是无法总结出一个规律,给人感觉起名称非常随意。就像上一代人,因为生活贫困,又生了许多孩子,没有精力和时间给孩子起一个再三斟酌名字,叫得应就行了,这种味道。
命令名称
Powershell从一开始就非常注重这一块内容,把起名当成和功能开发一样严肃对待。名称的规律是verb-noun,动词-名词。 Get-ChildItem(ls),Get-Content(cat),Set-Content翻译成中文的话,获取-子条目,获取-内容,设置-内容。*** 通过powershell的别名机制,ls,cat等都可以直接使用***
命令参数
命令的参数设计的非常巧妙,在参数位置输入“-”,然后按tab,所有的参数名称都会循环显示。除了参数名称的tab自动化,连参数值都可以tab化,参数值tab化的前提是参数值做了限定。比如 -Encoding (ascii|utf8),这种情况下,按tab键的时候,会在ascii,utf8之间循环显示。
在缺少某个“必须”参数的情况下,Powershell会提示你输入参数的值。
你得到的反馈是需要一个Value的参数,而且这个参数可以从管道获取。再仔细看,提示的是Value[0],说明也可以输入集合。这个问题后面详谈。
#使用管道
"abc" | Set-Content -Path abc.txt -Encoding Ascii
#直接参数
Set-Content -Path abc.txt -Value "abc" -Encoding Ascii
# 在尝试之前,想象一下下面的代码会产生什么结果。如果结果如你所想,至少这种设计是直观易懂的。
"abc", "def" | Set-Content -Path abc.txt -Encoding Ascii
里面流着“对象”的管道
Linux管道里面流的是一个单独的字符串,PowerShell里面是对象。请看下面的示例代码:
- 第一个命令: 列出当前目录下的文件和目录 --》不是容器(不是目录) --》文件名称不是以.开头 --》 列出5个即可。
- 第二个命令:开头和第一个命令一致,接下去看看对象有那些属性,这里指示列出最后5个属性,其中包括Length。
- 第三个命令按照长度逆向排序的结果。 从这个简单的示例中,应该能够体会“对象”的优势了。
*** 补充一下,这个选择文件可以用switcher参数,-File,图片中使用了 Where-Object PSIscontainer -EQ $False ***
命令return的是什么?
除了你显式地丢弃地内容,powershell捕获所有的输出。初次接触Powershell可能会非常的不适应,只要记住这个“捕获一切”,也算是一种简单的规律吧。
function Return-What {
"a"
1
"c"
}
# 返回的是一个对象数组,长度3
(Return-What).getType() # Object[]
function Return-What {
"a" | Out-Null
1
"c"
}
# 返回的是一个对象数组,长度2,因为"a"| Out-Null,显式地抛弃了输出。
function Return-What {
$a = 1
}
# 赋值语句不产生输出,没返回什么
(Return-What) -eq $null
这种规律即使在管道里面也严格遵守。
"a","b" | ForEach-Object -Begin {$a=@();"hello"} -Process {$a += $_; $_} -End {$a}
# 结果就是hello,a,b,a,b
几乎无差别的对待“单体”和“集合”
内置的命令都能合理的对待单体和集合,把单体想象成只有一个成员的集合,然后一切都似乎可以解释通了。
#比较一下下面的两行代码
Get-ChildItem . | Where-Object PSIscontainer -EQ $False | Where-Object Name -Match "\.bak$" | Remove-Item
# 把当前目录下以.bak结尾文件(不包含目录)删除,如果我想象正确的化,应该是每个对象流到Remove-Item的时候,进行删除。
Get-ChildItem . | Where-Object PSIscontainer -EQ $False | Where-Object Name -Match "\.bak$" | Select-Object -ExpandProperty FullName | Out-File -filepath mybakfiles.txt
# 把当前目录下的bak文件的全路径名称写入到mybakfiles.txt文件中,这个结果就有点类似于reduce的味道,将流的对象收集起来放到文件中。
用管道还是不用管道,这是一个问题吗?
Powershell除去管道就是java,C#了。Powershell具有完备的foreach,while,do-while等关键字,你可以像java一样书写代码。 但是本人觉得管道是powershell的精髓,它训练人的思维模式,一旦适应之后,就可以书写流畅、易读的代码。使用java的stream的时候是否有这种感觉呢?
比如确定一个集合的所有对象是否符合某种要求。
# 不用管道
$a = "etc/hadoop/hdfs-site.xml", "etc/hadoop/core-site.xml", "etc/hadoop/mapred-site.xml","etc/hadoop/yarn-site.xml", "abc/kkk.xml"
$allStartsWithEtc = $True
foreach ($i in $a) {
if (! $i.startsWith("etc/")) {
$allStartsWithEtc = $False
break;
}
}
If ($allStartsWithEtc) {
# do something.
}
# 使用管道
$a = "etc/hadoop/hdfs-site.xml", "etc/hadoop/core-site.xml", "etc/hadoop/mapred-site.xml","etc/hadoop/yarn-site.xml", "abc/kkk.xml"
if (!($a | Where-Object {$_ -notmatch "^etc/"} | select-object -first 1)) {
# do something
}
广告时间
EasyInstaller,使用web界面安装群集软件。脚本代码基于powershell和tcl,bash,欢迎脚本爱好者参与。