首页 > 解决方案 > PS1 替换或删除单个字符而不重写整个文件

问题描述

是否可以使用 PowerShell 1 删除或替换文件最后一个非空白行的最后一个字符?

我正在尝试获取精确到 5 分钟内的正常运行时间日志。

我发现有可以通过命令提示符访问的内置日志和命令,它们会告诉我计算机上次启动的时间或正确关闭的时间,但本机正常运行时间日志每 24 小时仅记录一次,所以如果出现电源故障,我将不知道系统离线了多久,比24小时更精确的精度。

所以我创建了以下脚本:

$bootTime = (Get-WmiObject Win32_OperatingSystem).LastBootUpTime
$formBootTime = [Management.ManagementDateTimeConverter]::ToDateTime($bootTime)
$uptime = (Get-Date)-$formBootTime
"$formBootTime,$(Get-Date),{0:00},{1:00},{2:00},{3:00}" -f $uptime.Days,$uptime.Hours,$uptime.Minutes,$uptime.Seconds >> C:\UptimeTracker.csv

但是,当我想评估我的机器在过去 X 天内运行了多长时间时,滚动浏览的时间会很长。

所以我想我会添加一个标记来识别每个给定引导的当前或最近的正常运行时间日志。

但是为了使它起作用,我需要能够在以前的记录不再是相关记录时立即删除所述标记。

$bootTime = (Get-WmiObject Win32_OperatingSystem).LastBootUpTime
$formBootTime = [Management.ManagementDateTimeConverter]::ToDateTime($bootTime)
$file = (Get-Content c:\UptimeTracker.csv | Measure-Object)
$numberOfLines = $file.Count
$numberOfWords = (Get-Content c:\UptimeTracker.csv | Select -Index ($numberOfLines -1) | Measure-Object -word)
$Line = Get-Content c:\UptimeTracker.csv | Select -Index ($numberOfLines -2)
$wordArray = $Line.Split(",")
$LastLineBT = $wordArray[0]
if($LastLineBT -eq $formBootTime) {
    $unmark = "true"
}
else
{$unmark = "false"}

if($unmark == "true"){ <remove last character of file> }

$uptime = (Get-Date)-$formBootTime 
"$formBootTime,$(Get-Date),{0:00},{1:00},{2:00},{3:00},X" -f $uptime.Days,$uptime.Hours,$uptime.Minutes, $uptime.Seconds >> C:\UptimeTracker.csv

以上部分内容借鉴和修改自:https ://stackoverflow.com/a/16210970/11035837

我已经看到了几种接收文件作为输入文件并写入不同输出文件的方法,从那里可以很容易地编写脚本重命名新文件和旧文件以切换它们的位置(新、旧、备用- 并旋转)我试图不重写整个文件的原因是减少命令/脚本被中断且操作未完成的那些实例。理想情况下,唯一没有完成操作的时间是电源故障。但是,我已经在以前的版本中看到过,它偶尔会跳过 5 分钟的间隔长达 15 分钟,而上次报告的启动时间没有任何变化。 我怀疑这与阻止任务调度程序运行脚本的其他更高优先级进程有关。 如果是这种情况,那么通过脚本完全重写文件失败的部分将丢失现有日志数据的一部分,我宁愿错过最新记录而不是所有数据。

我发现没有任何东西表明有任何能力删除/替换最后一个字符(或两个,因为一个是换行符),我也没有发现任何明确声明这是不可能的 - 我发现声明不可能选择性替换没有完全重写的内部或开始内容。

除非有任何解决方案确定的答案,或者如果确定的答案是否定的,这是无法做到的,那么我将尝试以下操作:

if($unmark == "true"){     
    $input = "C:\UptimeTracker_CUR.csv"
    $output = "C:\UptimeTracker_NEW.csv"

    $content = Get-Content $input
    $content[-2] = $content[-2] -replace 'X', ' '
    $content | Set-Content $output
    Rename-Item -Path "C:\UptimeTracker_CUR.csv" -NewName "C:\UptimeTracker_SBY.csv"
    Rename-Item -Path "C:\UptimeTracker_NEW.csv" -NewName "C:\UptimeTracker_CUR.csv"
}

编辑 - 由于 TheMadTechnician 的多读评论

...
$file = Get-Content c:\UptimeTracker.csv
$fileMeasure = ($file | Measure-Object)
$numberOfLines = $fileMeasure.Count
$numberOfWords = ($file | Select -Index ($numberOfLines -1) | Measure-Object -word)
$Line = $file | Select -Index ($numberOfLines -2)
...
...
if($unmark == "true"){     
    $output = "C:\UptimeTracker_NEW.csv"

    $file[-2] = $file[-2] -replace 'X', ' '
    $file | Set-Content $output
    Rename-Item -Path "C:\UptimeTracker.csv" -NewName "C:\UptimeTracker_SBY.csv"
    Rename-Item -Path "C:\UptimeTracker_NEW.csv" -NewName "C:\UptimeTracker.csv"
}

标签: powershell

解决方案


您多次阅读整个文件,这一定会减慢整个脚本的速度。我建议读入整个文件,确定是否需要清除标志,然后在输出时这样做,将新行添加到文件中。假设你还没有运行 PowerShell v2,你可以这样做:

$bootTime = (Get-WmiObject Win32_OperatingSystem).LastBootUpTime
$formBootTime = [Management.ManagementDateTimeConverter]::ToDateTime($bootTime)
$uptime = (Get-Date)-$formBootTime
$File = Get-Content c:\UptimeTracker.csv -raw
if($File.trim().split("`n")[-1].Split(',')[0] -eq $formBootTime){
    $File.trim() -replace 'X(?=\s*$)',' '
}else{
    $File.Trim()
},("$formBootTime,$(Get-Date),{0:00},{1:00},{2:00},{3:00},X" -f $uptime.Days,$uptime.Hours,$uptime.Minutes, $uptime.Seconds)|Set-Content c:\UptimeTracker.csv

如果您运行的是旧版本,您将无法-raw选择Get-Content. 作为一种解决方法,您可以改为执行此操作,并且相同的解决方案应该仍然有效。

$bootTime = (Get-WmiObject Win32_OperatingSystem).LastBootUpTime
$formBootTime = [Management.ManagementDateTimeConverter]::ToDateTime($bootTime)
$uptime = (Get-Date)-$formBootTime
$File = (Get-Content c:\UptimeTracker.csv) -Join "`n"
if($File.trim().split("`n")[-1].Split(',')[0] -eq $formBootTime){
    $File.trim() -replace 'X(?=\s*$)',' '
}else{
    $File.Trim()
},("$formBootTime,$(Get-Date),{0:00},{1:00},{2:00},{3:00},X" -f $uptime.Days,$uptime.Hours,$uptime.Minutes, $uptime.Seconds)|Set-Content c:\UptimeTracker.csv

这会变慢,所以应该被视为次要选项,因为您必须将整个文件作为字符串数组读取,并将其转换为单个多行字符串。


推荐阅读