web-dev-qa-db-fra.com

Remplacer la chaîne dans un fichier texte à l'aide de PowerShell

J'ai un fichier texte à un endroit du serveur. Ce fichier contient une valeur de chaîne et un horodatage . Je souhaite remplacer cette ligne spécifique par le dernier horodatage à l'aide de PowerShell.

Vous trouverez ci-dessous les données du fichier bloc-notes:

 Test Service | 05-09-2017 01-09-41 

Le 05-09-2017 le 01-09-41 est MM-jj-aaaa HH-mm-ss et je souhaite remplacer cette ligne par le dernier horodatage.

Donc, si après deux minutes il devrait être 

 Test Service | 05-09-2017 01-11-41 

Voici ce que je fais:

$ReplaceString = "$ServiceName + "|" + (Get-Date).ToString('MM-dd-yyyy HH-mm-ss')"
Get-Content Path -Replace($ServiceName + "|" + $StringLastRunTime,$ReplaceString) | Set-Content Path

Mais cela ne génère pas les résultats attendus.

METTRE À JOUR

Désolé de ne pas être aussi clair avec les exigences

Je souhaite surveiller les services critiques installés sur le serveur à l'aide du script PowerShell. J'ai répertorié tous les services à surveiller dans un fichier texte et ce chemin a été mentionné dans le script. Ce script s'exécutera toutes les 5 minutes. Si le service n'est pas installé ou s'il est arrêté, il génère une notification par courrier électronique. Pour éviter l'afflux d'emails, je pense mettre à jour un horodatage avec le nom du service dans le fichier texte . Donc, si je trouve un service en mode arrêté ou non installé, j'enverrai une notification et mettrai à jour l'horodatage de la dernière ça a été envoyé. J'ai un intervalle de renvoi qui vérifie toutes les cinq minutes et si cet intervalle est passé, je dois à nouveau envoyer la notification et je veux mettre à jour l'horodatage.

Code (J'ai supprimé la partie d'envoi d'email pour le rendre moins complexe):

$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."
        }
    }
}
8
Anand Shah

Vous pouvez le faire en utilisant un regex:

$filePath = 'yourPath'
(Get-Content $filePath -raw) -replace '([^|]+).*', ('$1|{0:MM-dd-yyyy HH-mm-ss}' -f (Get-Date)) |
    Set-Content $filePath

Explication regex :

 enter image description here

9
Martin Brandl

Je souhaite surveiller les services critiques installés sur le serveur à l'aide du script PowerShell.

Puisque cela semble être votre motivation pour faire tout ce script, je vais vous dire un meilleur moyen de surveiller les services que d'écrire des horodatages dans un fichier:

  • Ouvrir Voir les services locaux à partir du menu Démarrer.
  • Ouvrez la fenêtre des propriétés du service que vous souhaitez surveiller.
  • Sur l'onglet Récupération , configurez les options à votre guise pour les premier, deuxième et suivants échecs. Vous pouvez choisir de redémarrer un service défaillant et même d'exécuter un programme. Vous pouvez exécuter un script PowerShell comme suit: entrez PowerShell en tant que programme et c:\path\to\your\script.ps1 en tant que paramètre.
  • Dans ce script, vous pouvez simplement vérifier lequel des services importants n'est pas en cours d'exécution pour le moment.
3
Swonkie

Étant donné que votre fichier est utilisé comme base de données, vous devriez plutôt utiliser un stockage basé sur CSV, tout d'abord parce que vous avez des applets de commande conçues pour analyser des fichiers CSV, à savoir Import-CSV et Export-CSV, deuxièmement, car avec CSV, vous pouvez enregistrer des informations de type pour les colonnes. . De cette façon, vous pouvez générer le fichier CSV approprié une fois, puis exécuter Import-CSV, ce qui donne un tableau de PSObjects, puis traiter chacune d’elles en mettant à jour l’horodatage avec (get-date) ou [DateTime]::Now, puis en remplaçant Export-CSV les données enregistrées par une sauvegarde éventuelle, copie de travail. Comme ça:

$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

Avant cela, remplissez le fichier avec le contenu suivant, en fournissant les noms réels des services que vous souhaitez surveiller:

"Service","Timestamp"
"First service",""
"Second service",""
...
3
Vesper

En regardant votre code fourni et sans savoir quelle est la sortie réelle, je m'attendrais à ce que votre $ReplaceString soit erroné. Vos citations ne s'additionnent pas. Cela devrait fonctionner pour cela:

$ReplaceString = $ServiceName + "|" + (Get-Date).ToString('MM-dd-yyyy HH-mm-ss')
0
Seth

OK, alors votre exemple ...

$ReplaceString = "$ServiceName + "|" + (Get-Date).ToString('MM-dd-yyyy HH-mm-ss')"
Get-Content Path -Replace($ServiceName + "|" + $StringLastRunTime,$ReplaceString) | Set-Content Path

Pourrait probablement être corrigé. 

  1. La chaîne que vous recherchez avec -replace doit être une regex. Le caractère | est spécial dans une expression régulière et doit être échappé. Je composais le regex sur une ligne séparée et le stockais dans une variable nommée $ regex pour que je n'oublie pas.

  2. Vous voulez exécuter le remplacement sur chaque ligne sortant de Get-Content, vous devez donc le faire dans une étape de pipeline distincte, en utilisant% (abréviation de "foreach-object").

  3. J'utiliserais une interpolation de chaîne pour former les chaînes. C'est facultatif, mais je vais vous montrer ce que je veux dire ...

  4. Je présume que Path devrait être une variable et non le nom littéral du fichier.

  5. Obtenir et définir le contenu du même fichier dans un seul pipeline risque de générer un conflit. Le faire sur deux lignes différentes est le plus simple.

Une version corrigée pourrait ressembler à ceci:

$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

P.S. La partie [regex] :: Escape est juste au cas où ServiceName aurait des caractères spéciaux dedans ...
De plus, ce script ne garde pas le fichier verrouillé pendant la période qui s’écoule entre les deux dernières lignes, donc une autre application pourrait le mettre à jour entre les deux. Avec les fichiers texte, le dernier rédacteur l'emporte. Ainsi, en cas de collision, certaines mises à jour risquent d'être perdues.

0
Burt_Harris

Le code que vous avez ajouté à la description ne semble pas fonctionner. De plus, je suggère d'utiliser l'opérateur de formatage pour les chaînes.

$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;
0
rufer7