J'ai un gros fichier qui contient de nombreuses lignes. Par exemple:

ts=2019-01-16 network=1.1.1.1 system=irgendwas pid=100 bugReq=dasf something=else maybe=this

De chaque ligne, je souhaite extraire les informations suivantes:

ts=,system= & something=, mais les valeurs après = changent toujours.

J'ai essayé ceci, mais je n'ai pas pu le faire fonctionner:

$found = $string -match '.*system="(\d+)".*' if ($found) { $system= $matches[1]}
3
igge 27 janv. 2019 à 23:05

3 réponses

Meilleure réponse

Voici encore une autre solution. [ grin ] il utilise l'applet de commande ConvertFrom-StringData pour analyser l'entrée en objets. puis il crée un [PSCustomObject] avec uniquement les accessoires voulus. enfin, il envoie chaque objet à la collection $ Results.

Alors que la construction de l'objet personnalisé final rend les informations suivantes sans importance dans ce cas, il est important de savoir que la sortie de l'applet de commande ConvertFrom-StringData est une table de hachage standard. cela signifie que l'ordre des objets ne sera presque certainement PAS dans l'ordre d'origine. NE vous attendez PAS à ce que les choses soient dans l'ordre où elles apparaissent dans la source .

[edit = a ajouté une nouvelle ligne de données avec des espaces incorporés et un modèle -replace mis à jour pour gérer cela.]

# fake reading in a text file
#    in real life, use Get-Content
$InStuff = @(
    'ts=2019-01-16 network=1.1.1.1 system=irgendwas pid=100 bugReq=dasf something=else maybe=this'
    'ts=2019-01-16 network=1.1.1.2 system=PC-001 pid=100 bugReq=dasf something=OtherElse maybe=this'
    'ts=2019-01-16 network=1.1.1.66 system=PC-666 pid=100 bugReq=dasf something=ThisELse maybe=this'
    'ts=2019-01-16 network=1.1.1.3 system=PC-123 pid=100 bugReq=dasf something=AnotherElse maybe=this'
    'ts=2019-01-16 network=1.1.1.4 system=PC-004 Oo-LaLa another value with WhiteSpace id=100 bugReq=dasf something=Else-ish with Whitespace'
    )

$Results = foreach ($IS_Item in $InStuff)
    {
    # this requires that spaces ONLY be found as delimiters
    #    if you have embedded spaces, some sort of data format adjustment will be required
    #    now there is a need for handline embedded whitespace
    #$IS_Item -replace ' ', [environment]::NewLine |
    $IS_Item -replace '(\w{1,}=)', ('{0}{1}' -f [environment]::NewLine, '$1') |
        ConvertFrom-StringData |
        ForEach-Object {
            [PSCustomObject]@{
                TS = $_.ts
                System = $_.system
                Something = $_.something
                }
            }
    }

$Results

Sortie à l'écran ...

TS         System                                       Something               
--         ------                                       ---------               
2019-01-16 irgendwas                                    else                    
2019-01-16 PC-001                                       OtherElse               
2019-01-16 PC-666                                       ThisELse                
2019-01-16 PC-123                                       AnotherElse             
2019-01-16 PC-004 Oo-LaLa another value with WhiteSpace Else-ish with Whitespace

C'est une bonne collection d'objets simples, donc ça va Export-CSV assez bien. [ sourire ]

6
Lee_Dailey 28 janv. 2019 à 20:38

Donc, à quoi ressemble le journal que vous me montrez, il y a 3 sections que nous pouvons couper et coller à partir de Object, qui peuvent être retournées à partir de la nouvelle ligne Keypair, qui peuvent être retournées à partir de l'espace vide Key, qui peuvent être retournées en supprimant =

J'ai écrit une fonction pour ce cas en utilisant cette idée

function ConvertTo-PsObjectArrayList($Text,$TextObjectSeparator,$KeyPairSeparator,$KeySeparator){
    $ArrayList = New-Object System.Collections.ArrayList
    $TestData -split $TextObjectSeparator | %{
        $PsObject = new-object System.Management.Automation.PSObject
        $_ -split $KeyPairSeparator | %{      
            $KeyPair = $_ -split $KeySeparator
            $PsObject | Add-Member -MemberType NoteProperty -Name $KeyPair[0] -Value $KeyPair[1]
        }
        $ArrayList.Add($PsObject) | out-null
    }
    return $ArrayList
}

$TestData = @'
ts=2019-01-16 network=1.1.1.1 system=irgendwas pid=100 bugReq=dasf something=else maybe=this
ts=2019-01-16 network=1.1.2.1 system=irgendwas pid=130 bugReq=dasf something=else
ts=2019-01-16 network=1.1.1.1 system=irgendwas pid=150 bugReq=dasf something=else maybe=this
ts=2019-01-16 network=1.1.1.1 system=irgendwas pid=110 bugReq=dasf something=else
'@

ConvertTo-PsObjectArrayList -Text $TestData -TextObjectSeparator "`r`n" -KeyPairSeparator " " -KeySeparator "=" | select TS, System, Something

Cela reviendrait

ts         system    something
--         ------    ---------
2019-01-16 irgendwas else     
2019-01-16 irgendwas else     
2019-01-16 irgendwas else     
2019-01-16 irgendwas else
0
mklement0 28 janv. 2019 à 01:05

En supposant que la liste de paires clé-valeur de chaque ligne ne contienne que des valeurs sans espace blanc incorporé ni guillemets :

# Sample input line.
$line = 'ts=2019-01-16 network=1.1.1.1 system=irgendwas pid1=100 bugReq=dasf something=else maybe=this'

# Parse the line into key-value pairs and create a variable for each.
$i = 0
foreach ($keyOrValue in $line -split '[= ]') {
  if ($i++ % 2 -eq 0) { $varName = $keyOrValue }
  else                { Set-Variable $varName $keyOrValue }
}

# $ts now contains '2019-01-16', $network '1.1.1.1', $system 'irgendwas', ...

Notez comment j'ai légèrement modifié votre exemple de ligne d'entrée pour changer pid en pid1, car PowerShell ne vous permettra pas de créer une variable $PID, car il s'agit d'une automatique variable reflétant le PID de la session en cours (ID de processus).

Une autre option (qui éviterait également le conflit de nom de variable) est de créer une table de hachage pour chaque ligne d'entrée:

# Sample input line.
$line = 'ts=2019-01-16 network=1.1.1.1 system=irgendwas pid=100 bugReq=dasf something=else maybe=this'

# Parse the line into key-value pairs and create a variable for each.
$htValues = @{} # Initialize the hashtable.
$i = 0
foreach ($keyOrValue in $line -split '[= ]') {
  if ($i++ % 2 -eq 0) { $varName = $keyOrValue }
  else                { $htValues[$varName] = $keyOrValue }
}

# $htValues now has keys 'ts', 'network', 'system' with corresponding
# values, so you can access $htValues.ts to get '2019-01-16', for instance.

Cette approche a l'avantage supplémentaire de se prêter à collecter les tables de hachage créées pour les lignes individuelles dans un tableau global (par exemple, $hashTableArray = foreach ($line in ...) { ... } - mais avec un très gros fichier qui peut ne pas être une option.

En empruntant une idée à réponse de Lee_Dailey, vous pouvez également utiliser l'applet de commande ConvertFrom-StringData pour créer la table de hachage, après le premier placement chaque paire clé-valeur sur sa propre ligne à l'aide de l'opérateur -replace:

$htValues = ConvertFrom-StringData ($line -replace ' ', "`n")

La mise en garde concernant ConvertFrom-StringData est qu'il interprète \ caractères. comme séquences d'échappement de départ; par exemple, une valeur telle que b\c casse la commande:

Convertfrom-StringData 'a=b\c' # ERROR: "parsing 'b\c' - Missing control character."

Du côté positif, l'utilisation de ConvertFrom-StringData est beaucoup plus rapide que l'analyse manuelle avec foreach.


En passant: l'applet de commande Get-Content de PowerShell pour lire les lignes une par une (par défaut) est pratique, mais lente .

Pour traiter plus rapidement les lignes d'un (gros) fichier texte, utilisez:

$file = 'file.txt'
foreach ($line in [System.IO.File]::ReadLines((Convert-Path $file))) {
  # ...
}
1
mklement0 28 janv. 2019 à 14:05