初体验

修改某个目录下的文件名为小写。

1
Get-ChildItem | Rename-Item -NewName {$_.Name.ToLower()}
  1. get-childitem相当于ls,会返回一个array,里面item具体是什么类型。可以通过以下命令获取:

    1
    
    Get-ChildItem | Get-Member
    

    几乎所有的自带命令返回的都是一个array,所以我们可以调用获取item的数量。

    1
    2
    3
    4
    
    (get-childitem).length
    # 保存为变量,再取值
    $result = get-childitem
    $result.length
    
  2. PowerShell所有自带的命令都是Verb-Noun的命名方式,你可以通过get-command获取所有命令,我们可以看到有New-ItemNew-ItemProperty等。

  3. PowerShell所有自带的命令返回的一般是一个对象数组(an Array of objects),这和Bash总是返回字符串不同。所以PowerShell的管道接收也是一个对象数组,而不是字符串。

  4. 我们可以看到本例中,get-childitem的数组传递给了rename-item-newname接收一个脚本块。脚本块就是一个表达式,可以动态的计算结果。其中的$_指的是管道中的每一个item。$_.Name.ToLower()返回每一个小写后的文件名。举一反三的话,我们可以进行以下操作

    1
    2
    
    {$_.BaseName + '-备份' + $_.Extension } # 添加 -备份 作为后缀
    {$_.BaseName + '.txt'} # 修改后缀名为.txt
    

筛选

之前我们在给某个文件下的所有文件改名时,有时只想修改文件,排除文件夹,可以使用下列命令

1
2
3
4
5
6
7
8
9
get-childitem -file # 只选file
get-childitem -directory #只选directory
Get-ChildItem -Filter *.js # 所有.js结尾的
Get-ChildItem -Filter r????? # 只选r开头的6个字母的

get-childitem | where-object { $_.PSIsContainer} # 筛选文件夹
ls | ? {$_.psiscontainer} # 使用alias,?代表where-object
ls | ? { $_.psiscontainer -eq $false } # 筛选所有文件
ls | ? Name -Like *.js #直接使用property筛选
  1. 我们可以使用内置参数或者专门的筛选命令去筛选。优先使用内置参数,内置参数的性能更高。
  2. 很多参数比如-filter或者-name都支持使用通配符进行匹配,*星号可替代零个或多个字符,问号仅替代单个字符,比如love?.doc.
  3. where-object命令类似于数据库操作中的where,根据脚本块的布尔值或根据属性筛选。
  4. 后面的where-object简写成了?,这是alias,可以使用get-alias查询,用get-alias -definition where-object查询具体的命令

文本操作

有时我们需要将得到的结果输出为文本,比如列出所有文件名,并按指定格式存储为文本文件。

我们举一个例子,列出当前目录下的所有文件名(不包含文件夹)并按文件大小排序(由大到小),列出文件名和大小,只列出前5项

1
2
select-childitem -file | sort-object Length -Descending | Select-Object name,Length -First 5
ls -file | sort length | select name,length -first 5
  1. 我们使用sort-object根据length属性排序,默认升序(由小到大),使用降序
  2. 将排好序的数组传递给select-object,这个命令可以提取某些属性并创建一个仅有这些属性的对象,或者使用-first, -last, -unique, -index筛选对象。

结果输出

可以将上述结果输出到文本文件

1
2
3
4
5
6
ls -file > a.txt
ls -file | ForEach-Object { $_.Name + "`t" + $_.Length } > a.txt

ls -file | sort-object Length -Descending | ForEach-Object { $a = $_.Name + "`t" + $_.Length; $a + '后缀' } > a.txt
# use alias for foreach-object
ls -file | sort Length -Descending | % { $a = $_.Name + "`t" + $_.Length; $a + '后缀' } > a.txt
  1. 我们使用重定向符号输出到a.txt,第一个命令输出的类似于表格一样的格式,只适合查看
  2. 如果需要不带表头、不带空行的格式用来和外部程序交互的,可以使用第二种命令。foreach-object会对每个object执行脚本块。注意脚本块可以包含多个语句,用;隔开。
  3. 因为这个脚本块会收集所有的output,每一个语句会有output,可以分配给一个变量,或者用$null特殊变量(不用每次都想一个变量名)抑制输出。
  4. 本例中我们使用```t``表示制表符,这个backtick类似于其他语言中的\,所以换行符\n也是同理。

文本读取

读取某个文本文件,过滤出错误信息

1
2
Get-Content "log.txt" | Select-String "error"
Select-String -Path "*.txt" -Pattern "warning" 

操作注册表

378

在PowerShell中,注册表被当作文件系统来操作,通过HKLM:\或者HKCU:\操作

  1. HKLM: = HKEY_LOCAL_MACHINE(系统级)

  2. HKCU: = HKEY_CURRENT_USER(用户级)

  3. 创建注册表项(Key)

    1
    2
    
    # 创建项(-Force:不存在则自动创建父项)
    New-Item -Path HKCU:\Software\MyApp -Force
    
  4. 创建或设置注册表值

    1
    2
    3
    4
    5
    6
    7
    8
    
    # 字符串 (String)
    New-ItemProperty -Path HKCU:\Software\MyApp -Name Version -Value "1.0.0" -PropertyType String
    
    # 32位整数 (DWord)
    New-ItemProperty -Path HKCU:\Software\MyApp -Name Enabled -Value 1 -PropertyType DWord
    
    # -Force:不存在则创建(相当于新建+修改)
    Set-ItemProperty -Path HKCU:\Software\MyApp -Name NewSetting -Value 5 -Type DWord -Force
    
  5. 删除项或者值

    1
    2
    3
    4
    5
    6
    7
    8
    
    # 删除整个项(-Recurse -Force)
    Remove-Item -Path HKCU:\Software\MyApp -Recurse -Force
    
    # 删除单个值
    Remove-ItemProperty -Path HKCU:\Software\MyApp -Name OldValue
    
    # 静默删除(忽略不存在)
    Remove-ItemProperty -Path $path -Name $name -ErrorAction SilentlyContinue
    
  6. 查看项和读取值

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    # 列出指定项下所有子项
    Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion
    
    # 查看项本身
    Get-Item HKCU:\Software
    
    # 读取项的所有值
    Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion
    
    # 读取单个值(-Name)
    Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion -Name DevicePath
    
    (Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion -Name DevicePath)."DevicePath"
    
    # 只获取值数据(常用脚本写法)
    (Get-ItemProperty -Path $path -Name $name).$name
    
    # 检查项是否存在
    Test-Path HKCU:\Software\MyApp
    

wrapper脚本

将一个或多个命令行程序再包装,提前设置好对应的参数。我们以ffmpeg为例,如果要提取视频的音频,我们可以使用以下命令:

1
2
3
4
# 直接提取,不重新编码
ffmpeg -i input.mp4 -vn -c:a aac output.aac
# 使用quality 2 (0最好,9最差)转码成mp3
ffmpeg -i input.mp4 -vn -c:a libmp3lame -q:a 2 output.mp3

每次编写一大堆参数完全没有意义,我们希望有一个脚本convert input.mp4 input.mp3

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
param (
    [Parameter(Mandatory=$false)]
    [string]$inputPath,

    [Parameter(Mandatory=$false)]
    [string]$outputPath
)

# Show HELP if no input path provided
if (-not $InputPath) {
    Write-Host "`n=== FFmpeg Audio Extractor Wrapper (PowerShell)`n" -ForegroundColor Cyan
    Write-Host "Usage:" -ForegroundColor Yellow
    Write-Host "  .\Extract-Audio.ps1 -InputPath <VIDEO_FILE> [-OutputFormat <FORMAT>]`n"
    Write-Host "Parameters:"
    Write-Host "  -InputPath      Path to your video (required)"
    Write-Host "  -OutputFormat   Audio format (optional): AAC (default), MP3, WAV, FLAC`n"
    Write-Host "Examples:"
    Write-Host "  .\Extract-Audio.ps1 -InputPath video.mp4"
    Write-Host "  .\Extract-Audio.ps1 -InputPath movie.mkv -OutputFormat MP3`n"
    exit 0
}

# set output next to the video
if (-not $OutputPath) {
    $OutputPath = [io.path]::ChangeExtension($InputPath, ".aac")
    # $outputFile = Join-Path -Path $file.DirectoryName -ChildPath ($file.BaseName + ".aac")
}

# pass input to ffmpeg
ffmpeg -i $InputPath -vn -c:a aac $OutputPath