Matt W
Matt W

Reputation: 12434

Surrounding a string variable with quotes

I am writing an IIS log parser and having trouble wrapping a variable value in quotes while doing some string processing.

Here is a truncated log file, as an example:

#Fields:  date time s-ip cs-method ...
2021-08-09 19:00:16.367 0.0.0.0 GET ...
2021-08-09 19:01:42.184 0.0.0.0 POST ...

Here is how I am executing the code below:

.\Analyse.ps1 cs-method -eq `'POST`'

If the line marked with #PROBLEM is executed as is, the output looks like this:

> .\Analyse.ps1 cs-method -eq `'POST`'
"""""G""E""T""""" ""-""e""q"" ""'""P""O""S""T""'""
"""""P""O""S""T""""" ""-""e""q"" ""'""P""O""S""T""'""

But if I replace $quoted with $value, so that the code reads like this:

            $thisInstruction = $thisInstruction -replace $key , $value #PROBLEM

The output looks like this:

> .\Analyse.ps1 cs-method -eq `'POST`'
GET -eq 'POST'
POST -eq 'POST'

The problem is that I want the first value on each line of the output (the GET and the POST before the -eq) to be wrapped in quotes.

How can I achieve this?

Here is my code:

# compile cli args into single line instruction
$instruction = $args -join " "

# define key array
$keys = @('date','time','s-ip','cs-method','cs(Host)','cs-uri-stem','cs-uri-query','s-computername','s-port','cs-username','c-ip','s-sitename','cs(User-Agent)','cs(Referer)','sc-status','sc-substatus','sc-win32-status','TimeTakenMS','x-forwarded-for')

# <#
# get current execution folder
$currentFolder = Get-Location

# define string splitter regex https://www.reddit.com/r/PowerShell/comments/2h5elx/split_string_by_spaces_unless_in_quotes/ckpkydh?utm_source=share&utm_medium=web2x&context=3
$splitter = ' +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)'

# process *.log files in folder
Get-Childitem -Path $currentFolder *.log1 | ForEach-Object {
    # process each line in the file
    Get-Content $_.Name | ForEach-Object {
        # duplicate instruction
        $thisInstruction = $instruction

        # exclude comment lines
        if (!$_.StartsWith('#')) {
            # split line into array
            $logEntryArr = $_ -Split $splitter

            # populate dictionary with contents of array
            For ($i=0; $i -le $keys.length; $i++) {
                # get key
                $key = $keys[$i]
                # get value
                $value = $logEntryArr[$i]
                $quoted = "`""+$value+"`""
                
                # replace mention of key in instruction with dictionary reference
                $thisInstruction = $thisInstruction -replace $key , $quoted #PROBLEM
            }
            # process rule from command line against dictionary
            echo $thisInstruction
        }
    }
}
#>

Upvotes: 0

Views: 98

Answers (1)

Matt W
Matt W

Reputation: 12434

I do know why, thanks to @mathias-r-jessen commenting

I don't know why, but altering the For loop to iterate one fewer fixed the problem and does not appear to leave out any keys. The only significant change is this:

    For ($i=0; $i -le $keys.length-1; $i++) {

This PowerShell script can be used to query a folder of log files echo out matching rows, eg:

.\Analyse.ps1 cs-method -eq 'GET'

The above would print out all log entries with a cs-method value of GET.

Here's the code:

# compile cli args into single line instruction
$instruction = $args -join " "

# define key array
$keys = @('date','time','s-ip','cs-method','cs(Host)','cs-uri-stem','cs-uri-query','s-computername','s-port','cs-username','c-ip','s-sitename','cs(User-Agent)','cs(Referer)','sc-status','sc-substatus','sc-win32-status','TimeTakenMS','x-forwarded-for')

# get current execution folder
$currentFolder = Get-Location

# define string splitter regex https://www.reddit.com/r/PowerShell/comments/2h5elx/split_string_by_spaces_unless_in_quotes/ckpkydh?utm_source=share&utm_medium=web2x&context=3
$splitter = ' +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)'

# process *.log files in folder
Get-Childitem -Path $currentFolder *.log | ForEach-Object {
    # process each line in the file
    Get-Content $_.Name | ForEach-Object {
        # duplicate instruction
        $thisInstruction = $instruction

        # exclude comment lines
        if (!$_.StartsWith('#')) {
            # split line into array
            $logEntryArr = $_ -Split $splitter

            # populate dictionary with contents of array
            For ($i=0; $i -lt $keys.length; $i++) {
                # get key
                $key = $keys[$i]
                # get value
                $quoted = "`'"+$logEntryArr[$i]+"`'"
                
                # replace mention of key in instruction with dictionary reference
                $thisInstruction = $thisInstruction -replace $key , $quoted
            }
            # process rule from command line against dictionary
            $answer = Invoke-Expression $thisInstruction
            if ($answer) {
                echo $_
            }
        }
    }
}

Upvotes: 1

Related Questions