PowerShell命令与脚本(10)——脚本
编写和运行脚本
一个Powershell仅仅是一个包含Powershell代码的文本文件。如果这个文本文件执行,Powershell解释器会逐行解释并执行它的的语句。Powershell脚本非常像以前CMD控制台上的批处理文件。您可以通过非常简单的文本编辑工具创建Powershell脚本。
1、编写PowerShell脚本
- 通过重定向创建脚本
如果您的脚本不是很长,您甚至可以直接在控制台中要执行的语句重定向给一个脚本文件。
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625232757870-1203831381.jpg)
这样有个缺点,就是您的代码必须放在闭合的引号中。这样的书写方式一旦在脚本内部也有引号时,是一件很痛苦的事。甚至您还可能希望在脚本中换行。下面的Here-strings例子不错,也就是将脚本文件通过@‘ ’@闭合起来。
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625232805749-1409419137.jpg)
Here-String以 @‘开头,以’@结束.任何文本都可以存放在里面,哪怕是一些特殊字符,空号,白空格。但是如果您不小心将单引号写成了双引号,Powershell将会把里面的变量进行解析。
- 通过编辑器创建脚本
其实非常方便的还是最地道的文版编辑器Notepad,您可以直接在Powershell控制台中打开Notepad
notepad.exe .\MyScript.ps1
编辑完记得保存即可。
2、运行PowerShell脚本
当您的脚本编写成功后您可能第一次会像下面的方式运行它,也就是只输入脚本的文件名,会报错。
除非您使用相对路径,或者绝对路径。
执行策略限制
Powershell一般初始化情况下都会禁止脚本执行。脚本能否执行取决于Powershell的执行策略。
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625232820429-1629525285.jpg)
只有管理员才有权限更改这个策略。非管理员会报错。
查看脚本执行策略,可以通过 Get-ExecutionPolicy
更改脚本执行策略,可以通过 Set-ExecutionPolicy UnRestricted
脚本执行策略类型为:Microsoft.PowerShell.ExecutionPolicy
查看所有支持的执行策略:[System.Enum]::GetNames([Microsoft.PowerShell.ExecutionPolicy])
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625232837723-1709783077.jpg)
Unrestricted:权限最高,可以不受限制执行任何脚本。
Default:为Powershell默认的策略:Restricted,不允许任何脚本执行。
AllSigned:所有脚本都必须经过签名才能在运行。
RemoteSigned:本地脚本无限制,但是对来自网络的脚本必须经过签名。
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625232855369-1295318295.jpg)
给脚本传递参数
怎样将一个脚本稍作润色,让它能够根据用户的输入,处理并输出相应的结果,而不是只产生一成不变的输出。怎样将参数传递给脚本,这是本节讨论的内容。
1、$args返回所有的参数
传递给一个函数或者一个脚本的参数都保存在$args变量中。可以先打开记事本,输入脚本:
Write-Host "Hello,$args"
保存后,通过控制台执行脚本:
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625232916178-286320958.jpg)
2、$args数组参数
默认情况下,传递给一个Powershell脚本的参数类型为数组,例如:
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625232956362-1356770797.jpg)
上面的文本中包含多个连续的空格,可是当脚本把参数输出时却不存在连续的空格了。那是因为脚本会把文本根据白空格截断并转换成数组。如果不想文本被当成数组那就把它放在引号中。
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625233008836-1031527939.jpg)
在$args中逐个访问参数
因为$args是一个数组,自然可以通过索引访问数组的每一个元素。可以将MyScript.sp1的内容改为:
For($i=0;$i -lt $args.Count; $i++)
{
Write-Host "parameter $i : $($args[$i])"
}
然后在控制台测试:
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625233021179-2138482336.jpg)
3、在脚本中使用参数名
通过Powershell传递参数固然方便,但是如果用户不知道参数的传递顺序,也是很郁闷的,例如在Myscript.ps1中输入:
$args[0]-$args[1]
执行脚本发现参数的顺序不同,结果也不同:
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625233038598-468790226.jpg)
所以最好的方式给参数指定名称,输入以下的脚本:
param($Directory,$FileName)
"Directory= $Directory"
"FileName=$FileName"
其中param给参数指定名称。
执行脚本:
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625233051760-45022038.jpg)
4、验证参数
给脚本的参数绑定数据类型,绑定帮助信息。一旦脚本缺少参数,或者输入的参数类型不正确,就提醒用户:
输入脚本:
param(
[string]$Name=$(throw "Parameter missing: -name Name") ,
[int]$Age=$(throw "Parameter missing: -age x as number")
)
"Name= $Name"
"Age=$Age"
执行脚本:
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625233255876-2146914700.jpg)
5、变量的作用域
Powershell默认使用全局作用域global: ,但是在函数和脚本中分别使用函数作用域function:和脚本作用域script: 。
一旦脚本执行结束,存在于脚本作用域的变量也会消失。但是有一点,如果一个变量在脚本外定义,在脚本内没有定义,在脚本内使用时会把外面的变量引渡过来。
在脚本中输入:
$temp
执行脚本:
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625233352980-850409837.jpg)
在脚本中尝试改变变量$temp,但是脚本内的变量不会影响脚本外的变量,输入脚本:
$temp="powershell is soooooo good"
$temp
执行脚本:
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625233331817-404802736.jpg)
PowerShell增强脚本的可读性
如果你愿意,你可以把一个脚本写的非常长,问题是脚本的代码量越大,可读性越差。最好的方式在写脚本时融入函数和类库的概念:
函数:把实现一些小功能的代码写成一个函数,不仅可以增强代码的可读性,还可以很方便的重用。一旦你创建了一个实现特定功能的函数,也可以下次在其它脚本中使用。
类库:把需要的函数嵌入进类库中,就不用每次在执行脚本时拷贝函数,并且还可以在需要时扩充它。另外以函数的方式构建类库,还可以让你更专注特定功能的具体实现,降低脚本开发的复杂度。
1、在脚本中使用函数
要在脚本中使用函数,最简单的方法自然是将函数直接写在脚本中:
在MyScript.ps1中输入:
param([int]$n=$(throw "请输入一个正整数"))
Factorial $n
Function Factorial([int]$n)
{
$total=1
for($i=1;$i -le $n;$i++)
{
$total*=$i
}
return $total
}
这个脚本接收一个正整数参数,然后通过Factorial函数求阶乘。
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625233447923-96306586.jpg)
不像其它脚本语言,Powershell中的函数必须先定义后使用。
所以更改脚本为:
param([int]$n=$(throw "请输入一个正整数"))
Function Factorial([int]$n)
{
$total=1
for($i=1;$i -le $n;$i++)
{
$total*=$i
}
return $total
}
Factorial $n
执行脚本:
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625233529542-615443824.jpg)
2、将脚本分为工作脚本和类库
真正的脚本开发需要处理的问题可能包含许多函数。如果在一个脚本的开头定义许多函数,脚本会显得很凌乱。把函数和工作脚本分开,可以隔离函数,使它不容易被修改。
将Factorial函数保存在PSLib.ps1
Function Factorial([int]$n)
{
$total=1
for($i=1;$i -le $n;$i++)
{
$total*=$i
}
return $total
}
将MyScript.ps1脚本修改为:
param([int]$n=$(throw "请输入一个正整数"))
. .\PSLib.ps1
Factorial $n
执行脚本:
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625233641409-742630577.jpg)
脚本在执行时,先加载类库中的函数。加载函数类库和执行脚本类似,只需要在前面增加一个句号,中间有空格。
3、类库脚本集中存放
在开始使用类库脚本工作之前,最好先制定出一个存储脚本类库的策略。一种方法是和工作脚本存放在一起,可以使用相对路径;另一种方法是分开存放,加载时就得使用绝对路径了。最好在当前用户的私人目录中存放脚本,相对来说比较安全。
创建管道脚本
我们可以像创建管道函数那样创建管道脚本,具体采用低速顺序模式,还是高速流模式,这取决于具体的编程实现。
1、低速顺序模式
如果你在脚本中使用管道,脚本收集上一个语句的执行结果,默认保存在$input自动变量中。但是直到上一条语句完全执行彻底,管道脚本才会执行。
创建脚本:
pipeline.ps1
foreach($element in $input)
{
if($element.Extension -eq ".exe")
{
Write-Host -fore "red" $element.Name
}
else
{
Write-Host -fore "Green" $element.Name
}
}
执行脚本:
ls $env:windir | .pipeline.ps1
如果这样执行:
ls $env:windir -Recurse | .pipeline.ps1
控制台会被冻结,因为存储的中间结果在玩命的吃内存。这个也是低速顺序模式的缺点。
2、高速流模式
在Powershell脚本的处理中,绝大多数情况下遇到的都是集合,一旦上一条命令产生一个中间结果,下一条命令就对这个中间结果及时处理,及时释放资源。这样可以节省内存,也减少了用户的等待时间。在处理大量数据时,尤其值得推荐。高速流模式的管道定义包括三部分:begin,process,end。上面的描述中提到了中间结果,中间结果保存在$_自动化变量中。
begin
{
Write-Host -fore "Green" "管道脚本环境初始化"
}
process
{
$ele=$_
if($_.Extension -ne "")
{
switch($_.Extension.tolower())
{
".ps1" {"ps1脚本文件:"+$ele.name}
".vbs" {"vbs脚本文件:"+$ele.name}
".txt" {"txt文本文件:"+$ele.name}
}
}
}
end
{
Write-Host -fore "Yellow" "管道脚本环境恢复"
}
执行脚本文件:
![](https://img2020.cnblogs.com/blog/1942601/202106/1942601-20210625233658071-387964963.jpg)
自动执行脚本之profile
在Powershell控制台的许多更改只会在当前会话有效。一旦关闭当前控制台,你自定义地所有别名、函数、和其它改变将会消失,除非将更改保存在windows环境变量中。这也就是为什么我们需要profile来保存一些基本的初始化工作。
四中不同的profile脚本
Powershell支持四种可以用来初始化任务的profile脚本。应用之前要弄清楚你的初始化是当前用户个人使用,还是所有用户。如果是个人使用,可以使用”当前用户profile“,但是如果你的初始化任务是针对所有用户,可是使用“所有用户profile”。
我们注意到上面的四种profile有两个private。一旦声明为private,只有个microsoft的Powershell自身才会去调用,不会对其它引用powershell的组件有效。
创建自己的profile
Profile脚本并不是强制性的,换言之,profile可有可无。下面会很方便的创建自己的profile。 在控制台执行: notepad $profile 如果不存在profile默认会创建,在打开的记事本中输入: Set-Alias edit notepad.exe
也就是给notepad添加edit别名,保存关闭,之后重启控制台,输入: edit
控制台会打开记事本打开,可见edit别名已经生效。
创建全局profile
创建全局的profile也是很容易的,如上,只是文件的位置稍有改变; 需要注意的是,创建全局profile需要管理员权限,没有管理员权限,该文件或者文件夹拒绝访问。还有一点也须注意:在vista系统中,即使你拥有管理员权限,但是没有通过administrator登录,并且系统没有禁用UAC,也是拒绝更改的。除非你鼠标右键单击Powershell快捷方式,以管理员权限运行。