[玩转系统] 应对 PowerShell 链接挑战
作者:精品下载站 日期:2024-12-14 08:00:26 浏览:14 分类:玩电脑
应对 PowerShell 链接挑战
几周前,Iron Scripter 面临的挑战是将符合某些标准的文件移动到新位置并留下链接。正如我之前所写,这些挑战是测试您的 PowerShell 技能和拓展自我的好方法。这项挑战有许多活动部分。
通常,使用 PowerShell 时最困难的工作是规划和组织。你将如何完成给定的任务?如果您正在编写其他人将使用的代码,他们会有什么期望或假设?他们如何使用你的命令?我还考虑灵活性和重用。我尽量避免编写一个可以做几件事的单一函数。相反,您应该专注于可以组合在管道表达式中的小型一次性任务。或者从控制器脚本进行编排,这就是我稍后将向您展示的内容。
测试核心命令
此任务中的核心命令是 New-Item。您可以使用此 cmdlet 创建文件、文件夹和链接。我假设您会查看完整的帮助和示例。我来试一下。
New-Item -ItemType SymbolicLink -Path . -Name procmon.exe -Value D:\OneDrive\tools\Procmon.exe
此命令将在当前目录中创建指向 D:\OneDrive\tools\Procmon.exe 的链接。
我可以在 C:\Work 中运行 procmon.exe,而无需关心实际文件所在的位置。出色的。挑战的前提是浏览文件列表,将它们移动到目的地并留下链接。
移动和链接
这听起来像是我可能想重复使用的任务。我不想限制要处理的文件。我希望能够将任何文件传递给我的命令并让它移动和链接。从技术上讲,PowerShell 有一个 Move-Item 命令,但我将“移动和链接”过程视为单个操作,因此我将以这种方式对其进行编码。另外,我还想解决一些其他具有挑战性的功能。
这是我的“移动和链接”功能。
#requires -version 5.1
Function Move-FileWithLink {
[CmdletBinding(SupportsShouldProcess)]
Param(
[Parameter(Position = 0, Mandatory, HelpMessage = "Specify the path of a file to move.", ValueFromPipelineByPropertyName)]
[alias("fullname")]
[ValidateNotNullOrEmpty()]
[ValidateScript({ Test-Path $_ })]
[string]$Path,
[Parameter(HelpMessage = "Specify the top-level target path. It will be created if it doesn't exist.")]
[ValidateNotNullOrEmpty()]
[string]$Destination = "\172.16.10.100\backup\lts",
[switch]$Passthru
)
Begin {
Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)"
#define a timestamped logfile
$logname = "{0}_MoveLink.log" -f (Get-Date -Format "yyyy_MM-dd-hhmm")
#using .NET to support cross-platform
$logfile = Join-Path -Path $([System.io.path]::GetTemppath()) -ChildPath $logname
Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Logging activity to $logfile"
#define a logging helper function
function log {
[cmdletbinding(SupportsShouldProcess)]
param([string]$Message, [string]$LogFile)
$text = "[{0}] {1}" -f (Get-Date -Format u), $Message
$text | Out-File -FilePath $logFile -Force -Append
}
#set the default parameter value for the log function
$PSDefaultParameterValues["log:logfile"] = $logfile
log "Starting: $($myinvocation.MyCommand)"
$who = "$([System.Environment]::UserDomainName)$([System.Environment]::UserName)"
$where = [System.Environment]::MachineName
log "User: $who"
log "Computer: $where"
if (-Not (Test-Path -Path $Destination)) {
Try {
log "Creating $destination"
[void](New-Item -ItemType Directory -Path $Destination -Force -ErrorAction Stop)
}
Catch {
Throw $_
}
}
} #begin
Process {
$Path = Convert-Path $path
Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Processing $Path"
log "Processing $path"
$parent = Split-Path -Path $path
$name = Split-Path -Path $path -Leaf
#get the relative path so it can be reconstructed in the destination
$target = Join-Path -Path $Destination -ChildPath $parent.Substring(3)
if ($pscmdlet.ShouldProcess($path, "Move file to $target")) {
Try {
#recreate file structure
if (-Not (Test-Path -Path $Target)) {
Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Creating $Target"
[void](New-Item -ItemType Directory -Path $Target -Force -ErrorAction Stop)
log "Created target $target"
}
Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Moving file to $Target"
$m = Move-Item -Path $path -Destination $target -PassThru -ErrorAction Stop
log "Moved $path to $target"
}
Catch {
$msg = "Failed to move $path to $target. $($_.Exception.message)"
Write-Warning $msg
log $msg
}
}
Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Creating link for $path"
if ($pscmdlet.ShouldProcess($path, "Creating link")) {
if (Test-Path $m.fullname) {
Try {
$link = New-Item -ItemType SymbolicLink -Name $name -Path $parent -Value $m.FullName -Force -ErrorAction Stop
log "Created symboliclink from $($m.fullname)"
if ($passthru) {
$link
}
}
Catch {
$msg = "Failed to create link to $path from $($m.fullname). $($_.Exception.message)"
Write-Warning $msg
log $msg
}
}
}
} #process
End {
log "Ending: $($myinvocation.MyCommand)"
if (Test-Path $logfile) {
Write-Verbose "[$((Get-Date).TimeofDay) END ] Activity logged at $logfile"
}
if ($PSDefaultParameterValues.ContainsKey("log:logfile")) {
$PSDefaultParameterValues.Remove("log:logfile")
}
Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)"
} #end
}
该函数将采用文件名作为参数,将其复制到目标,然后在原始位置创建链接。该函数具有硬编码的日志记录功能,它将在用户的 %TEMP% 文件夹中创建基于文本的日志文件。
$logname = "{0}_MoveLink.log" -f (Get-Date -Format "yyyy_MM-dd-hhmm")
#using .NET to support cross-platform
$logfile = Join-Path -Path $([System.io.path]::GetTemppath()) -ChildPath $logname
function log {
[cmdletbinding(SupportsShouldProcess)]
param([string]$Message, [string]$LogFile)
$text = "[{0}] {1}" -f (Get-Date -Format u), $Message
$text | Out-File -FilePath $logFile -Force -Append
}
我编写了一个简短的辅助函数,以便每个日志条目都包含 UTC 时间戳。我想让我的代码保持简单,所以我临时为该函数设置了一个 PSDefaultParameterValue。
#set the default parameter value for the log function
$PSDefaultParameterValues["log:logfile"] = $logfile
log "Starting: $($myinvocation.MyCommand)"
这使得在整个脚本中记录事件变得容易。在函数末尾,我删除了 PSDefaultParameterValue。
if ($PSDefaultParameterValues.ContainsKey("log:logfile")) {
$PSDefaultParameterValues.Remove("log:logfile")
}
该函数使用 cmdletbinding 属性来启用 -WhatIf。尽管我使用的大多数命令都会自动检测 -WhatIf,但我选择创建自己的 -WhatIf 代码,以便对消息传递有更多控制。
if ($pscmdlet.ShouldProcess($path, "Move file to $target")) {
Try {
#recreate file structure
if (-Not (Test-Path -Path $Target)) {
Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Creating $Target"
[void](New-Item -ItemType Directory -Path $Target -Force -ErrorAction Stop)
log "Created target $target"
}
Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Moving file to $Target"
$m = Move-Item -Path $path -Destination $target -PassThru -ErrorAction Stop
log "Moved $path to $target"
}
Catch {
$msg = "Failed to move $path to $target. $($_.Exception.message)"
Write-Warning $msg
log $msg
}
}
我可以像这样使用该函数:
还可以检测日志记录功能的 WhatIf。我将移动并链接该文件。
这就是我日志中的最终内容。
[2020-12-02 16:16:37Z] Starting: Move-FileWithLink
[2020-12-02 16:16:37Z] User: PROSPERO\Jeff
[2020-12-02 16:16:37Z] Computer: PROSPERO
[2020-12-02 16:16:37Z] Creating d:\archive
[2020-12-02 16:16:37Z] Processing C:\work\s.clixml
[2020-12-02 16:16:37Z] Created target d:\archive\work
[2020-12-02 16:16:37Z] Moved C:\work\s.clixml to d:\archive\work
[2020-12-02 16:16:37Z] Created symboliclink from D:\archive\work\s.clixml
[2020-12-02 16:16:37Z] Ending: Move-FileWithLink
使用控制器脚本
现在我有了一个可以移动和链接文件的工作工具,我需要一种简单的方法来使用它。这就是考虑谁将使用您的命令以及如何发挥作用的地方。移动和链接功能将接受任何输入。经验丰富的 PowerShell 用户可以简单地运行如下命令:
但是,挑战在于寻找一种用户可以在指定位置运行的工具。我们面临的挑战还在于寻找一种工具来移动和链接在给定日期之前最后修改的文件。我编写了一个控制器脚本。
此类脚本旨在包装一些核心命令并以结构化方式执行它们。您可以手动运行脚本中的命令并获得相同的结果。控制器脚本还可以使用参数以及函数中几乎所有的脚本功能。
#requires -version 5.1
<#
move specified files to an alternate location, leaving
linked copies behind
Specify the source folder
Specify the target folder
Specify the last modified age
Support recursion
Support an exclude filter
This could be turned into a function instead of a control script
#>
[CmdletBinding(SupportsShouldProcess)]
Param (
[parameter(Position = 0, Mandatory, HelpMessage = "The folder to process for old files.", ValueFromPipeline)]
[ValidateScript({Test-Path $_ })]
[string]$Path,
[parameter(Position = 1, Mandatory, HelpMessage = "The top-level destination folder.")]
[string]$Destination,
[Parameter(HelpMessage = "Specify the last modified cutoff date.")]
[datetime]$LastModified = (Get-Date).AddDays(-180).Date,
[parameter(HelpMessage = "Recurse for files from the given path.")]
[switch]$Recurse,
[parameter(HelpMessage = "Specify a file pattern to exclude. Wildcards are permitted.")]
[string]$Exclude,
[switch]$Passthru
)
Begin {
Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)"
#dot source the function that moves files and creates links
. $PSScriptRoot\Move-FileLink.ps1
$getParams = @{
ErrorAction = "Stop"
File = $True
}
if ($Recurse) {
$getParams.Add("Recurse",$True)
}
$moveParams = @{
Destination = $Destination
Passthru = $Passthru
}
Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Finding files last modified before $LastModified"
if ($Exclude) {
Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Excluding files that match the pattern $Exclude"
}
Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Using a destination of $destination"
} #begin
Process {
#convert to a complete file-system path
$Path = Convert-Path $Path
Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Processing $Path"
$getParams.Path = $Path
#build a filter for Where-Object. -Exclude only works with Get-Childitem when using
#recurse, which the user might not want.
if ($Exclude) {
Get-ChildItem @getParams | Where-object {($_.LastWriteTime -lt $LastModified) -AND ($_.name -notlike $Exclude)} |
Move-FileWithLink @moveParams
}
else {
Get-ChildItem @getParams | Where-Object { $_.LastWriteTime -lt $LastModified } | Move-FileWithLink @moveParams
}
} #process
End {
Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)"
} #end
如果我愿意的话,将其转换为函数并不需要太多。事实上,我经常从独立脚本开始,因为我可以运行它们而无需额外的点源步骤。一旦我对代码感到满意,添加功能位就很简单了。
让我们看看这个脚本的实际效果。
详细输出清楚地表明了该命令将要执行的操作。
概括
核心命令灵活且可重用,超出了挑战要求。其余的挑战要求在控制脚本中得到满足。我想很多人都忽略了这个概念。并非所有内容都必须放入函数中。控制脚本是确保一致性的好方法。您可以对这些脚本进行签名,将它们签入源代码管理,并以与处理 PowerShell 模块相同的方式管理它们。
如果您觉得需要提高 PowerShell 脚本编写技能,请考虑获取一本《PowerShell 脚本编写和工具制作书籍》。
猜你还喜欢
- 03-30 [玩转系统] 如何用批处理实现关机,注销,重启和锁定计算机
- 02-14 [系统故障] Win10下报错:该文件没有与之关联的应用来执行该操作
- 01-07 [系统问题] Win10--解决锁屏后会断网的问题
- 01-02 [系统技巧] Windows系统如何关闭防火墙保姆式教程,超详细
- 12-15 [玩转系统] 如何在 Windows 10 和 11 上允许多个 RDP 会话
- 12-15 [玩转系统] 查找 Exchange/Microsoft 365 中不活动(未使用)的通讯组列表
- 12-15 [玩转系统] 如何在 Windows 上安装远程服务器管理工具 (RSAT)
- 12-15 [玩转系统] 如何在 Windows 上重置组策略设置
- 12-15 [玩转系统] 如何获取计算机上的本地管理员列表?
- 12-15 [玩转系统] 在 Visual Studio Code 中连接到 MS SQL Server 数据库
- 12-15 [玩转系统] 如何降级 Windows Server 版本或许可证
- 12-15 [玩转系统] 如何允许非管理员用户在 Windows 中启动/停止服务
取消回复欢迎 你 发表评论:
- 精品推荐!
-
- 最新文章
- 热门文章
- 热评文章
[影视] 黑道中人 Alto Knights(2025)剧情 犯罪 历史 电影
[古装剧] [七侠五义][全75集][WEB-MP4/76G][国语无字][1080P][焦恩俊经典]
[实用软件] 虚拟手机号 电话 验证码 注册
[电视剧] 安眠书店/你 第五季 You Season 5 (2025) 【全10集】
[电视剧] 棋士(2025) 4K 1080P【全22集】悬疑 犯罪 王宝强 陈明昊
[软件合集] 25年6月5日 精选软件22个
[软件合集] 25年6月4日 精选软件36个
[短剧] 2025年06月04日 精选+付费短剧推荐33部
[短剧] 2025年06月03日 精选+付费短剧推荐25部
[软件合集] 25年6月3日 精选软件44个
[剧集] [央视][笑傲江湖][2001][DVD-RMVB][高清][40集全]李亚鹏、许晴、苗乙乙
[电视剧] 欢乐颂.5部全 (2016-2024)
[电视剧] [突围] [45集全] [WEB-MP4/每集1.5GB] [国语/内嵌中文字幕] [4K-2160P] [无水印]
[影视] 【稀有资源】香港老片 艺坛照妖镜之96应召名册 (1996)
[剧集] 神经风云(2023)(完结).4K
[剧集] [BT] [TVB] [黑夜彩虹(2003)] [全21集] [粤语中字] [TV-RMVB]
[实用软件] 虚拟手机号 电话 验证码 注册
[资源] B站充电视频合集,包含多位重量级up主,全是大佬真金白银买来的~【99GB】
[影视] 内地绝版高清录像带 [mpg]
[书籍] 古今奇书禁书三教九流资料大合集 猎奇必备珍藏资源PDF版 1.14G
[电视剧] [突围] [45集全] [WEB-MP4/每集1.5GB] [国语/内嵌中文字幕] [4K-2160P] [无水印]
[剧集] [央视][笑傲江湖][2001][DVD-RMVB][高清][40集全]李亚鹏、许晴、苗乙乙
[电影] 美国队长4 4K原盘REMUX 杜比视界 内封简繁英双语字幕 49G
[电影] 死神来了(1-6)大合集!
[软件合集] 25年05月13日 精选软件16个
[精品软件] 25年05月15日 精选软件18个
[绝版资源] 南与北 第1-2季 合集 North and South (1985) /美国/豆瓣: 8.8[1080P][中文字幕]
[软件] 25年05月14日 精选软件57个
[短剧] 2025年05月14日 精选+付费短剧推荐39部
[短剧] 2025年05月15日 精选+付费短剧推荐36部
- 最新评论
-
- 热门tag