使用PowerShell替换文本文件中的字符串
我在服务器上的某个位置有一个文本文件。该文件包含一个字符串值和一个时间戳。 我想用PowerShell替换这个特定的行和最新的时间戳。使用PowerShell替换文本文件中的字符串
下面是记事本文件中的数据:
Test Service|05-09-2017 01-09-41
这里2017年5月9日41年1月9日是MM-DD-YYYY HH-MM-SS,我想与最新的替换此行时间戳。
所以,如果两分钟后它应该是
Test Service|05-09-2017 01-11-41
下面是我在做什么:
$ReplaceString = "$ServiceName + "|" + (Get-Date).ToString('MM-dd-yyyy HH-mm-ss')"
Get-Content Path -Replace($ServiceName + "|" + $StringLastRunTime,$ReplaceString) | Set-Content Path
但它不会产生预期的输出。
UPDATE
对不起,我不是符合要求
我想监控安装使用PowerShell脚本在服务器上的关键服务,以便清楚。我在一个文本文件中列出了要监视的所有服务,并且该脚本中提到了该路径。该脚本每5分钟执行一次,如果未安装服务或停止服务,则会生成电子邮件通知。为了防止电子邮件泛滥,我正考虑使用文本文件中的服务名称来更新时间戳。 因此,如果我发现一个服务处于停止模式或未安装,我将发送通知并更新最后发送时间的时间戳。我有一个重发间隔,每隔五分钟检查一次,如果间隔时间已过,我必须再次发送通知,我想更新时间戳。
代码(我已删除的电子邮件发送部分,使其不太复杂):
$ServicesList = Get-Content E:\ServiceReports\Services.txt
$ResendEmailInterval = 10 #In Minutes
foreach ($Service in $ServicesList) {
$ServiceName = $Service
$Service = Get-Service -Display $Service -ErrorAction SilentlyContinue
$serviceDisplayName = $Service.displayname
if (-Not $Service) {
#Service is not installed. So need to send an email alert and update the timestamp.
Write-Host $ServiceName.IndexOf("|")
if ($ServiceName.IndexOf("|") -gt 0) {
$ServiceName,$StringLastRunTime = $ServiceName.Split('|')
$LastRunTime = [datetime]::ParseExact($StringLastRunTime, "MM-dd-yyyy HH-mm-ss", $null)
Write-Host $ServiceName
Write-Host $LastRunTime
Write-Host (Get-Date).AddMinutes($ResendEmailInterval)
if ($LastRunTime -lt (Get-Date).AddMinutes($ResendEmailInterval)) {
Write-Host "time to run"
$ReplaceString = $ServiceName + "|" + (Get-Date).ToString('MM-dd-yyyy HH-mm-ss')
Write-Host $ServiceName"|"$StringLastRunTime
Write-Host $ReplaceString
#$ServiceName = "Test Service";
$ReplaceString = "{0}|{1}" -f $ServiceName, (Get-Date).ToString('MM-dd-yyyy HH-mm-ss');
(Get-Content -Path $ServicesListPath) -replace ('([^|]+).*', $ReplaceString) | Set-Content $ServicesListPath;
$ServicesList -replace ($ServiceName + "|" + $StringLastRunTime, $ReplaceString) | Set-Content $ServicesListPath
}
} else {
$ReplaceString = $ServiceName + "|" + (Get-Date).ToString('MM-dd-yyyy HH-mm-ss')
$ServicesList -replace ($ServiceName, $ReplaceString) | Set-Content $ServicesListPath
$i = $i+1;
}
} else {
if ($Service.Status -eq "Stopped") {
Write-Host "Service is stopped"
#Time to send email and update the time stamp
$i = $i+1;
} else {
#Write-Host "Service is running. Nothing to do."
}
}
}
看你提供的代码,并在不知道实际的输出是什么,我希望你$ReplaceString
是错误。你的报价不加起来。这应该为它工作:
$ReplaceString = $ServiceName + "|" + (Get-Date).ToString('MM-dd-yyyy HH-mm-ss')
你可以做到这一点使用一个正则表达式:
$filePath = 'yourPath'
(Get-Content $filePath -raw) -replace '([^|]+).*', ('$1|{0:MM-dd-yyyy HH-mm-ss}' -f (Get-Date)) |
Set-Content $filePath
你能解释我的正则表达式部分 –
@RanadipDutta检查我添加到答案的链接。 –
谢谢.. @ Martin –
您添加到说明的代码似乎不上班。此外,我建议对字符串使用格式化运算符。
$ServiceName = "Test Service";
$ReplaceString = "{0}|{1}" -f $ServiceName, (Get-Date).ToString('MM-dd-yyyy HH-mm-ss');
(Get-Content -Path $Path) -replace ('([^|]+).*', $ReplaceString) | Set-Content $Path;
我已经用更多的描述,代码和用例更新了这个问题 –
由于您的文件作为一个数据库,你应该使用一个基于CSV存储,首先是因为你需要解析的CSV到的cmdlet来回回,即Import-CSV
和Export-CSV
,第二因为CSV你能够保存列的类型信息。这样你就可以生成正确的CSV一次,然后运行Import-CSV
产生一个PSObject
的数组,然后你处理它们中的每一个用(get-date)
或[DateTime]::Now
更新时间戳,然后你覆盖保存的数据,并可能一起保存为备份与工作副本。就像这样:
$ServicesList=Import-CSV E:\ServiceReports\Services.txt -encoding UTF8
foreach ($service in $serviceslist) {
# a PSObject expected to have "Service" and "Timestamp" fields
$servicename=$service.service
$ts=$service.timestamp
if ($ts -eq "") { $ts=get-date "01.01.2000 00:00:00" }
$sysservice=get-service -displayname $servicename -ea silentlycontinue
if (-not $sysservice) {
if ($ts -lt [DateTime]::Now.AddMinutes(-1*$ResendEmailInterval)) {
# send email - no service
$service.timestamp=[DateTime]::Now # just that
}
} elseif ($sysservice.status -eq 'Stopped') {
write-host "Service $servicename is stopped"
if ($ts -lt [DateTime]::Now.AddMinutes(-1*$ResendEmailInterval)) {
# send email - service stopped
$service.timestamp=[DateTime]::Now # update timestamp
}
} else {
#service is running, set timestamp to "zero"
$service.timestamp=get-date "01.01.2000 00:00:00"
}
}
$ServicesList | Export-CSV E:\ServiceReports\Services.txt -encoding UTF8
在此之前,填入下列内容的文件,你靶向服务监控提供实际的名称:
"Service","Timestamp"
"First service",""
"Second service",""
...
我想监控安装关键服务在使用PowerShell脚本的服务器上。
因为这似乎是你的动机做这一切的脚本,我要告诉你一个更好的方式来监控服务比写时间戳到一个文件:
- 打开查看本地服务从开始菜单。
- 打开要监视
- 在恢复选项卡服务,设立了第一,第二和随后的失败的选项根据自己的喜好的属性窗口。您可以选择重新启动失败的服务,甚至运行程序。您可以像这样运行PowerShell脚本:输入
PowerShell
作为程序,输入c:\path\to\your\script.ps1
作为参数。 - 在该脚本中,您可以简单地检查当前哪些重要服务未运行。
好了,你的榜样......
$ReplaceString = "$ServiceName + "|" + (Get-Date).ToString('MM-dd-yyyy HH-mm-ss')"
Get-Content Path -Replace($ServiceName + "|" + $StringLastRunTime,$ReplaceString) | Set-Content Path
很可能是固定的。
您用
-replace
搜索的字符串需要是正则表达式。|
字符在正则表达式中是特殊的,需要被转义。我会在单独的行上编写正则表达式,并将其存储在名为$ regex的变量中,这样我就不会忘记。您希望在Get-Content的每一行上运行替换,因此需要在单独的管道步骤中使用%(“foreach-object”的缩写)完成替换。
我会使用字符串插值来形成字符串。这是可选的,但我会告诉你我的意思...
我认为
Path
应该是一个变量,而不是文件的文字名称。在单个管道中获取和设置同一文件的内容很可能会导致冲突。在两条不同的路线上进行操作最简单。
一个固定的版本可能是这个样子:
$regex = "^$([regex]::Escape($serviceName))\|.*"
$ReplaceString = "${ServiceName}|$((Get-Date).ToString('MM-dd-yyyy HH:mm:ss'))"
$result = Get-Content $Path | %{ $_ -replace($regex,$ReplaceString)}
Set-Content $Path -Value $result
附: [正则表达式] :: Escape部分是为了防止ServiceName中有特殊字符...
此外,此脚本不会在最后两行执行时间段内保持文件锁定,所以其他一些应用程序可能会在之间进行更新。使用文本文件,最后一位作者会获胜,所以如果发生冲突,某些更新可能会丢失。
这是由什么产生的实际输出? – Seth
你可以粘贴整个脚本吗? – 30000MONKEYS
@ 30000MONKEYS更新 –