当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 周五乐趣:PowerShell 难题

作者:精品下载站 日期:2024-12-14 07:38:58 浏览:13 分类:玩电脑

周五乐趣:PowerShell 难题


[玩转系统] 周五乐趣:PowerShell 难题

这不会取代我的日历和任务列表,但它让我可以直接从 PowerShell 中查看我不想错过的事件。当我研究这个想法时,我最终得到了一个 PowerShell 模块 MyTickle.psm1,它有许多用于管理痒痒事件的函数,正如我所说的那样。我可以从 PowerShell 获取事件、添加、设置和删除。我认为该模块会成为一个很好的星期五有趣的帖子,因为它当然不是一个高影响力的项目,但它提供了一些关于构建模块和功能的想法,我希望您会发现这些想法有用。

现在的模块是一个文件。这是文件,下面我将讨论。

#requires -version 3.0

#  ****************************************************************
#  * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED *
#  * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK.  IF   *
#  * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, *
#  * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING.             *
#  ****************************************************************

#MYTICKLE.PSM1
#Last updated 5/17/2013

#region Define module variables
#This should be the WindowsPowerShell folder under your Documents folder
$profileDir = Split-Path $profile

#the path to the tickle csv file
$TicklePath = Join-Path -Path $profileDir -ChildPath "mytickler.csv"

#the default number of days to display
$TickleDefaultDays = 7 
#endregion

#region Define module functions

Function Get-TickleEvent {

<#

.Synopsis
Get Tickle Events
.Description
Get tickle events by id or name. The default behavior is to get all events in
date order. You can specify a range of ID numbers and use wildcards with the 
event names. Use the Expired switch to get all tickle events that have already 
occurred. 

The command will not throw an exception if no matching tickle events are found.

#>

[cmdletbinding(DefaultParameterSetname="ALL")]
Param(
[Parameter(Position=0,ParameterSetName="ID")]
[int[]]$Id,
[Parameter(Position=0,ParameterSetName="Name")]
[string]$Name,
[Parameter(Position=0,ParameterSetName="Expired")]
[switch]$Expired,
[ValidateScript({Test-Path $_} )]
[string]$Path=$TicklePath
)

Write-Verbose "Importing events from $Path"

Switch ($pscmdlet.ParameterSetName) {
 "ID"      {
            Write-Verbose "by ID" 
            $filter = [scriptblock]::Create("`$_.id -in `$id")  }
 "Name"    { 
            Write-Verbose "by Name"
            $filter = [scriptblock]::Create("`$_.Event -like `$Name") }
 "Expired" { 
            Write-Verbose "by Expiration"
            $filter = [scriptblock]::Create("`$_.Date -lt (Get-Date)") }
 "All"     { 
            Write-Verbose "All"
            $filter = [scriptblock]::Create("`$_ -match '\w+'") }

} 

#import CSV and cast properties to correct type
Import-CSV -Path $Path | 
Select @{Name="ID";Expression={[int]$_.ID}},
@{Name="Date";Expression={[datetime]$_.Date}},
Event,Comment | where $Filter | Sort date

} #Get-TickleEvent

Function Set-TickleEvent {

<#

.Synopsis
Set a tickle event
.Description
This command will update the settings for a tickle event. You can update the
event name, date or comment. The easiest way to use this is to pipe an tickle
event to this command.

#>

[cmdletbinding(SupportsShouldProcess,DefaultParameterSetName="Inputobject")]
Param(
[Parameter(Position=0,Mandatory,HelpMessage="Enter the tickle event id",ParameterSetName="ID")]
[int]$Id,
[Parameter(Position=1,ValueFromPipeline,ParameterSetname="Inputobject")]
[object]$Inputobject,
[datetime]$Date,
[string]$Event,
[string]$Comment,
[ValidateScript({Test-Path $_} )]
[string]$Path=$TicklePath,
[switch]$Passthru
)
Begin {
    write-verbose "Using $($PSCmdlet.ParameterSetName) parameter set"
}
Process {

#if ID only then get event from CSV
Switch ($pscmdlet.ParameterSetName) {
 "ID" {
    Write-Verbose "Getting tickle event id $ID"
    $myevent = Get-TickleEvent -id $id
   }
 "Inputobject" {
    Write-Verbose "Modifying inputobject"
    $myevent = $Inputobject
 }
} #switch

#verify we have an event to work with
if ($myevent) {
    #modify the tickle event object
    write-verbose ($myevent | out-string)

    if ($Date) {
      Write-Verbose "Setting date to $date"
      $myevent.date = $Date
    }
    if ($Event) {
      Write-Verbose "Setting event to $event"
      $myevent.event = $Event
    }
    if ($comment) {
      Write-verbose "Setting comment to $comment"
      $myevent.comment = $comment
    }
    write-verbose "Revised: $($myevent | out-string)"

    #find all lines in the CSV except the matching event
    $otherevents = get-content -path $Path | where {$_ -notmatch "^""$($myevent.id)"} 
    #remove it
    $otherevents | Out-File -FilePath $Path -Encoding ascii 

    #append the revised event to the csv file
    $myevent | Export-Csv -Path $Path -Encoding ASCII -Append -NoTypeInformation

    if ($passthru) {
        $myevent
    }
}
else {
    Write-Warning "Failed to find a valid tickle event"
}

} #process

} #Set-TickleEvent

Function Add-TickleEvent {

<#
.Synopsis
Add a tickle event
.Description
This command will create a new tickle event. If the CSV file referenced by the
TicklePath variable does not exist, it will be created. You must enter an event 
name and date.
#>

[cmdletbinding(SupportsShouldProcess)]

Param (
[Parameter(Position=0,ValueFromPipelineByPropertyName,Mandatory,HelpMessage="Enter the name of the event")]
[string]$Event,
[Parameter(Position=1,ValueFromPipelineByPropertyName,Mandatory,HelpMessage="Enter the datetime for the event")]
[datetime]$Date,
[Parameter(Position=2,ValueFromPipelineByPropertyName)]
[string]$Comment,
[ValidateNotNullorEmpty()]
[string]$Path=$TicklePath,
[switch]$Passthru
)

Begin {
    #verify the path and create the file if not found
    if (! (Test-Path $Path)) {
        Write-Verbose "Creating a new file: $Path"
        Try {
         '"id","Date","Event","Comment"' | 
         Out-File -FilePath $Path -Encoding ascii -ErrorAction Stop
        }
        Catch {
            Write-Warning "Failed to create $Path"
            Write-Warning $_.Exception.Message
            $NoFile = $True
        }
    }
}

Process {
if ($NoFile) {
    Write-Verbose "No CSV file found."
    #bail out of the command
    Return
}

#get last id and add 1 to it
[int]$last = Import-Csv -Path $Path | 
Sort {$_.id -AS [int]} | Select -last 1 -expand id
[int]$id = $last+1

$hash=[ordered]@{
  ID = $id
  Date = $date
  Event = $event
  Comment = $comment
}
Write-Verbose "Adding new event"
Write-Verbose ($hash | out-string)

$myevent = [pscustomobject]$hash
$myevent | Export-Csv -Path $Path -Append -Encoding ASCII -NoTypeInformation
if ($passthru) {
    $myevent
}
} #process

} #Add-TickleEvent

Function Remove-TickleEvent {
<#
.Synopsis
Remove a tickle event
.Description
Remove one or more events from the tickle file. This will overwrite the current
file so you might want to back it up first with Backup-TickleFile.
.Example
PS C:\> get-ticklevent -expired | remove-tickleevent
#>

[cmdletbinding(SupportsShouldProcess,DefaultParameterSetName="Inputobject")]
Param(
[Parameter(Position=0,Mandatory,HelpMessage="Enter the tickle event id",ParameterSetName="ID")]
[int]$Id,
[Parameter(Position=1,ValueFromPipeline,ParameterSetname="Inputobject")]
[object]$Inputobject,
[ValidateScript({Test-Path $_} )]
[string]$Path=$TicklePath
)

Process {

    #if ID only then get event from CSV
    Switch ($pscmdlet.ParameterSetName) {
     "ID" {
        Write-Verbose "Getting tickle event id $ID"
        $myevent = Get-TickleEvent -id $id
       }
     "Inputobject" {
        Write-Verbose "Identifying inputobject"
        $myevent = $Inputobject
     }
    } #switch

    #verify we have an event to work with
    if ($myevent) {
        Write-Verbose "Removing event"
        Write-Verbose ($myEvent | Out-String)
        if ($pscmdlet.ShouldProcess(($myEvent | Out-String))) {
        #find all lines in the CSV except the matching event
        $otherevents = Get-Content -path $Path | where {$_ -notmatch "^""$($myevent.id)"} 
        #remove it
        $otherevents | Out-File -FilePath $Path -Encoding ascii 
        }
    } #if myevent

} #process

} #Remove-TickleEvent

Function Show-TickleEvent {
<#
.Synopsis
Display Tickle events in the console
.Description
This command gets upcoming tickle events and writes them to the console using
Write-Host. Use this command in your PowerShell profile script.
#>

[cmdletbinding()]
Param(
[Parameter(Position=0)]
[ValidateScript({Test-Path $_})]
[string]$Path=$TicklePath,
[Parameter(Position=1)]
[ValidateScript({$_ -ge 1})]
[int]$Days = $TickleDefaultDays
)

#import events from CSV file
$events = Import-Csv -Path $Path

#get upcoming events within 7 days sorted by date
$upcoming = $events | 
where {
 #get the timespan between today and the event date
 $ts = (New-TimeSpan -Start (Get-Date) -end $_.Date).TotalHours 
 #find events less than the default days value and greater than 0
 Write-Verbose $ts
 $ts -le ($Days*24) -AND $ts -gt 0
 } |
Add-Member -MemberType ScriptProperty -Name Countdown -value {New-TimeSpan -start (Get-Date) -end $this.date} -PassThru -force|
sort CountDown

if ($upcoming) {
#how wide should the box be?
#get the length of the longest line
$l = 0
foreach ($item in $upcoming) {
 #turn countdown into a string without the milliseconds
  $count = $item.countdown.ToString()
  $time = $count.Substring(0,$count.lastindexof("."))
  #add the time as a new property
  $item | Add-Member -MemberType Noteproperty -name Time -Value $time
  $a = "$($item.event) $($item.Date) [$time]".length
  if ($a -gt $l) {$l = $a}
  $b = $item.comment.Length
  if ($b -gt $l) {$l = $b}
}

[int]$width = $l+5

$header="* Reminders $((Get-Date).ToShortDateString())"

#display events
Write-Host "`r"
Write-host "$($header.padright($width,"*"))" -ForegroundColor Cyan
Write-Host "*$(' '*($width-2))*" -ForegroundColor Cyan

foreach ($event in $upcoming) {

  if ($event.countdown.totalhours -le 24) {
    $color = "Red"
  }
  elseif ($event.countdown.totalhours -le 48) {
    $color = "Yellow"
  }
  else {
    $color = "Green"
  }

  #define the message string
  $line1 = "* $($event.event) $($event.Date) [$($event.time)]"
  if ($event.comment -match "\w+") {
   $line2 = "* $($event.Comment)"
   $line3 = "*"
  }
  else {
   $line2 = "*"
   $line3 = $null
  }

$msg = @"
$($line1.padRight($width-1))*
$($line2.padright($width-1))*
"@

if ($line3) {
    #if there was a comment add a third line that is blank
    $msg+="`n$($line3.padright($width-1))*"
}

  Write-Host $msg -ForegroundColor $color

} #foreach

Write-host ("*"*$width) -ForegroundColor Cyan
Write-Host "`r"
} #if upcoming events found
else {
  $msg = @"

**********************
* No event reminders *
**********************

"@
  Write-host $msg -foregroundcolor Green
}

} #Show-TickleEvent

Function Backup-TickleFile {
<#
.Synopsis
Create a backup of the tickle file
.Description
This command will create a backup copy of the tickle CSV file. The default path
is the same directory as the tickle file. You might want to backup the tickle
file before removing any events.
#>

[cmdletbinding(SupportsShouldProcess)]
Param(
[ValidateScript({Test-Path $_} )]
[string]$Path=$TicklePath,
[ValidateScript({Test-Path $_} )]
[string]$Destination = (Split-Path $TicklePath),
[switch]$Passthru
)

Try {
    $ticklefile = Get-Item -Path $path
    $backup = Join-Path -path $Destination -ChildPath "$($ticklefile.basename).bak"
    Write-Verbose "Copying $path to $backup"
    $ticklefile | Copy-Item  -Destination $backup -ErrorAction Stop -PassThru:$Passthru
}
Catch {
    Write-Warning "Failed to backup file"
    Write-Warning $_.exception.message
}
} #Backup-TickleFile

#endregion

#region Define module aliases
Set-Alias -Name gte -value Get-TickleEvent
Set-Alias -name ate -Value Add-TickleEvent
Set-Alias -name rte -Value Remove-TickleEvent
Set-Alias -name ste -Value Set-TickleEvent
Set-Alias -name shte -Value Show-TickleEvent
Set-Alias -name btf -Value Backup-Ticklefile
#endregion

Export-ModuleMember -Function * -Variable TicklePath,TickleDefaultDays -Alias *

您应该能够从 WordPress 插件复制代码并将其粘贴到本地脚本文件中。您可以随意命名它,只需记住使用 .psm1 文件扩展名即可。该模块使用一些 PowerShell 3.0 功能,例如有序哈希表,但您可以进行修改以使其在 PowerShell 2.0 中运行。从根本上来说,它应该在两个版本中都可以工作。

这些事件存储在一个 CSV 文件中,我使用模块变量 $TicklePath 引用该文件。默认文件是名为 mytickler.csv 的文件,该文件位于“文档”下的 WindowsPowerShell 文件夹中。该模块还定义了一个名为 $TickleDefaultDays 的变量,默认值为 7。这会向属于该范围内的事件显示事件。为了使用,我将这些行添加到我的 PowerShell 配置文件中。

import-module c:\scripts\mytickle.psm1
show-tickleevent

结果是,当我启动新的 PowerShell 会话时,我看到类似这样的内容(有关帮助更新的消息来自其他内容,因此请忽略):

[玩转系统] 周五乐趣:PowerShell 难题

这是它的工作原理。

Show-TickleEvent 函数从 CSV 文件导入未来 7 天内将发生的事件。每个对象还获得一个附加属性,即剩余时间的时间跨度对象。然后该函数解析事件信息并在事件周围构造一个“盒子”。

#how wide should the box be?
#get the length of the longest line
$l = 0
foreach ($item in $upcoming) {
 #turn countdown into a string without the milliseconds
  $count = $item.countdown.ToString()
  $time = $count.Substring(0,$count.lastindexof("."))
  #add the time as a new property
  $item | Add-Member -MemberType Noteproperty -name Time -Value $time
  $a = "$($item.event) $($item.Date) [$time]".length
  if ($a -gt $l) {$l = $a}
  $b = $item.comment.Length
  if ($b -gt $l) {$l = $b}
}

[int]$width = $l+5

$header="* Reminders $((Get-Date).ToShortDateString())"

#display events
Write-Host "`r"
Write-Host "$($header.padright($width,"*"))" -ForegroundColor Cyan
Write-Host "*$(' '*($width-2))*" -ForegroundColor Cyan

#get upcoming events within 7 days sorted by date
$upcoming = $events |
where {
#get the timespan between today and the event date
$ts = (New-TimeSpan -Start (Get-Date) -end $_.Date).TotalHours
#find events less than the default days value and greater than 0
Write-Verbose $ts
$ts -le ($Days*24) -AND $ts -gt 0
} |
Add-Member -MemberType ScriptProperty -Name Countdown -value {New-TimeSpan -start (Get-Date) -end $this.date} -PassThru -force |
sort CountDown

我根据事件的迫近程度设置前景色,然后将每个事件写入控制台,并包裹在边框中。

#define the message string
  $line1 = "* $($event.event) $($event.Date) [$($event.time)]"
  if ($event.comment -match "\w+") {
   $line2 = "* $($event.Comment)"
   $line3 = "*"
  }
  else {
   $line2 = "*"
   $line3 = $null
  }

$msg = @"
$($line1.padRight($width-1))*
$($line2.padright($width-1))*
"@

if ($line3) {
    #if there was a comment add a third line that is blank
    $msg+="`n$($line3.padright($width-1))*"
}

  Write-Host $msg -ForegroundColor $color

我特意使用 Write-Host,以便可以对事件进行颜色编码,并且因为我不希望配置文件向管道写入任何内容。由于该模块是在 PowerShell 会话开始时加载的,因此我始终可以运行 Show-TickleEvent 并为事件指定不同的天数。如果我想要对象,那么我可以使用 Get-TickleEvent 函数,该函数将根据 ID 或名称等条件导入 csv 事件。该函数使用参数集,我根据参数集创建一个过滤器脚本块。

Switch ($pscmdlet.ParameterSetName) {
 "ID"      {
            Write-Verbose "by ID" 
            $filter = [scriptblock]::Create("`$_.id -in `$id")  }
 "Name"    { 
            Write-Verbose "by Name"
            $filter = [scriptblock]::Create("`$_.Event -like `$Name") }
 "Expired" { 
            Write-Verbose "by Expiration"
            $filter = [scriptblock]::Create("`$_.Date -lt (Get-Date)") }
 "All"     { 
            Write-Verbose "All"
            $filter = [scriptblock]::Create("`$_ -match '\w+'") }

}

当我导入 CSV 文件时,我将类型添加到属性中,否则所有内容都将是字符串,然后将每个对象通过管道传输到我的过滤器。

#import CSV and cast properties to correct type
Import-CSV -Path $Path | 
Select @{Name="ID";Expression={[int]$_.ID}},
@{Name="Date";Expression={[datetime]$_.Date}},
Event,Comment | where $Filter | Sort Date

这些对象非常有用,因为它们可以通过管道传输到 Set-TickleEvent 来修改事件名称、日期或注释等值。或者我可以通过管道发送到Remove-TickleEvent 来删除条目。删除过程本质上是查找 CSV 文件中所有不以正确 id 开头的行,并使用相同的名称创建一个新文件。

if ($pscmdlet.ShouldProcess(($myEvent | Out-String))) {
  #find all lines in the CSV except the matching event
  $otherevents = Get-Content -path $Path | where {$_ -notmatch "^""$($myevent.id)"} 
  #remove it
  $otherevents | Out-File -FilePath $Path -Encoding ascii 
 }

最后,在意外删除事件文件后,我添加了一个简单的备份功能,将 CSV 文件复制到同一目录,但文件扩展名为 .BAK。您可以指定备用路径,但默认为 WindowsPowerShell 文件夹。

希望我不会再错过重要的事件,当然假设我将它们添加到我的备忘文件中。我将让您使用 Add-TickleEvent 来了解其工作原理,或者您始终可以使用记事本修改 CSV 文件。

如果你真的使用这个,我希望你能告诉我。

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯