Century10. Underthewire.tech walkthrough - powershell

The password for Century10 is the 161st word within the file on the desktop.
NOTE:
- The password will be lowercase no matter how it appears on the screen.
*The question above is where i am facing my challenges. I tried the command below. *
Get-Content C:\Users\Century9\Desktop\Word_File.txt | Select-Object -Index 161
Result was nil. I understand that i need to assign a value to the string as it is now seen as one whole entity. But how do i do it ?

If the token of interest is the 161st word in the file, use the following approach, which splits the file into words irrespective of line breaks[1]:
$pass = (-split (Get-Content -Raw Word_File.txt))[160]
Append .ToLower() if you want to convert the token to all-lowercase.
Note that the above loads the entire file into memory as a single string, using -Raw.
Since array indices are 0-based, it is index [160] that returns the 161st element.
The unary form of the -split operator splits the input into an array of tokens by whitespace.
Note: If you want to split by the stricter definition of what constitutes a word in a regular-expression context, use the following instead:
$pass = ((Get-Content -Raw Word_File.txt) -split '\W+' -ne '')[160]
[1] If your input file contains each word on its own line:
Your solution was on the right track, except that you should pass 160 to Select-Object -Index, because the -Index parameter expects 0-based indices, not 1-based line numbers:
# Extract the 161st line.
$pass = Get-Content Word_File.txt | Select-Object -Index 160
To convert to lowercase:
$pass = (Get-Content Word_File.txt | Select-Object -Index 160).ToLower()
The above will fail if the input file has fewer than 161 lines (with error message You cannot call a method on a null-valued expression).
If you prefer to receive no output quietly instead, use the following (which uses built-in aliases select for Select-Object and foreach for ForEach-Object for brevity):
$pass = Get-Content Word_File.txt | select -Index 160 | foreach ToLower

Try running this:
((Get-Content -Path C:\Users\Century9\Desktop\Word_File.txt -TotalCount 161)[-1]).ToLower()

Related

Get-Content returns array or string

I am trying to check the beginning of the first 2 lines of a text file.
$ascii = Get-Content -LiteralPath $path -Encoding ascii -TotalCount 2
if ($ascii[0].StartsWith("aa")) {}
if ($ascii[1].StartsWith("bb")) {}
This works fine except if the file has only 1 line.
Then it seems to return a string rather than an array of strings, so the indexing pulls out a character, not a string.
Error if file has only 1 line:
Method invocation failed because [System.Char] doesn't contain a method named 'StartsWith'.
How can I detect if there are too few lines? $ascii.Length is no help as it returns the number of characters if there is only one line!
From about_Arrays:
Beginning in Windows PowerShell 3.0, a collection of zero or one object has the Count and Length property. Also, you can index into an array of one object. This feature helps you to avoid scripting errors that occur when a command that expects a collection gets fewer than two items.
I see you have tagged your question with powershell-2.0, if you're actually running this version of PowerShell, you would need to use the Array subexpression operator #( ) or type cast [array] for below example on how you can approach the problem at hand:
$ascii = Get-Content -LiteralPath $path -Encoding ascii -TotalCount 2
if($ascii.Count -gt 1) {
# This is 2 lines, your original code can go here
}
else {
# This is a single string, you can use a different approach here
}
For PowerShell 2.0, the first line should be either:
$ascii = #(Get-Content -LiteralPath $path -Encoding ascii -TotalCount 2)
Or
# [object[]] => Should also work here
[array]$ascii = Get-Content -LiteralPath $path -Encoding ascii -TotalCount 2

Sort-Object -Unique

I'm making a script that collects all the subkeys from a specific location and converts the REG_BINARY keys to text, but for some reason I can't remove the duplicate results or sort them alphabetically.
PS: Unfortunately I need the solution to be executable from the command line.
Code:
$List = ForEach ($i In (Get-ChildItem -Path 'HKCU:SOFTWARE\000' -Recurse)) {$i.Property | ForEach-Object {([System.Text.Encoding]::Unicode.GetString($i.GetValue($_)))} | Select-String -Pattern ':'}; ForEach ($i In [char[]]'ABCDEFGHIJKLMNOPQRSTUVWXYZ') {$List = $($List -Replace("$i`:", "`n$i`:")).Trim()}; $List | Sort-Object -Unique
Test.reg:
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\SOFTWARE\000\Test1]
"HistorySZ1"="Test1"
"HistoryBIN1"=hex:43,00,3a,00,5c,00,54,00,65,00,73,00,74,00,5c,00,44,00,2e,00,\
7a,00,69,00,70,00,5c,00,00,00,43,00,3a,00,5c,00,54,00,65,00,73,00,74,00,5c,\
00,43,00,2e,00,7a,00,69,00,70,00,5c,00,00,00,43,00,3a,00,5c,00,54,00,65,00,\
73,00,74,00,5c,00,42,00,2e,00,7a,00,69,00,70,00,5c,00,00,00,43,00,3a,00,5c,\
00,54,00,65,00,73,00,74,00,5c,00,41,00,2e,00,7a,00,69,00,70,00,5c,00,00,00
[HKEY_CURRENT_USER\SOFTWARE\000\Test2]
"HistorySZ2"="Test2"
"HistoryBIN2"=hex:4f,00,3a,00,5c,00,54,00,65,00,73,00,74,00,5c,00,44,00,2e,00,\
7a,00,69,00,70,00,5c,00,00,00,43,00,3a,00,5c,00,54,00,65,00,73,00,74,00,5c,\
00,43,00,2e,00,7a,00,69,00,70,00,5c,00,00,00,44,00,3a,00,5c,00,54,00,65,00,\
73,00,74,00,5c,00,42,00,2e,00,7a,00,69,00,70,00,5c,00,00,00,41,00,3a,00,5c,\
00,54,00,65,00,73,00,74,00,5c,00,41,00,2e,00,7a,00,69,00,70,00,5c,00,00,00
The path strings that are encoded in your array of bytes are separated with NUL characters (code point 0x0).
Therefore, you need to split your string by this character into an array of individual paths, on which you can then perform operations such as Sort-Object:
You can represent a NUL character as "`0" in an expandable PowerShell string, or - inside a regex to pass to the -split operator - \0:
# Convert the byte array stored in the registry to a string.
$text = [System.Text.Encoding]::Unicode.GetString($i.GetValue($_))
# Split the string into an *array* of strings by NUL.
# Note: -ne '' filters out empty elements (the one at the end, in your case).
$list = $text -split '\0' -ne ''
# Sort the list.
$list | Sort-Object -Unique
After many attempts I discovered that it is necessary to use the Split command to make the lines break and thus be able to organize the result.
{$List = ($List -Replace("$i`:", "`n$i`:")) -Split("`n")}

Performing A String Operation in a -replace Expression

I'm trying to make using of String.Substring() to replace every string with its substring from a certain position. I'm having a hard time figuring out the right syntax for this.
$dirs = Get-ChildItem -Recurse $path | Format-Table -AutoSize -HideTableHeaders -Property #{n='Mode';e={$_.Mode};width=50}, #{n='LastWriteTime';e={$_.LastWriteTime};width=50}, #{n='Length';e={$_.Length};width=50}, #{n='Name';e={$_.FullName -replace "(.:.*)", "*($(str($($_.FullName)).Substring(4)))*"}} | Out-String -Width 40960
I'm referring to the following expression
e={$_.FullName -replace "(.:.*)", "*($(str($($_.FullName)).Substring(4)))*"}}
The substring from the 4th character isn't replacing the Full Name of the path.
The paths in question are longer than 4 characters.
The output is just empty for the Full Name when I run the script.
Can someone please help me out with the syntax
EDIT
The unaltered list of strings (as Get-ChildItem recurses) would be
D:\this\is\where\it\starts
D:\this\is\where\it\starts\dir1\file1
D:\this\is\where\it\starts\dir1\file2
D:\this\is\where\it\starts\dir1\file3
D:\this\is\where\it\starts\dir1\dir2\file1
The $_.FullName will therefore take on the value of each of the strings listed above.
Given an input like D:\this\is or D:\this\is\where, then I'm computing the length of this input (including the delimiter \) and then replacing $_.FullName with a substring beginning from the nth position where n is the length of the input.
If input is D:\this\is, then length is 10.
Expected output is
\where\it\starts
\where\it\starts\dir1\file1
\where\it\starts\dir1\file2
\where\it\starts\dir1\file3
\it\starts\dir1\dir2\file1
If you want to remove a particular prefix from a string you can do so like this:
$prefix = 'D:\this\is'
...
$_.FullName -replace ('^' + [regex]::Escape($prefix))
To remove a prefix of a given length you can do something like this:
$len = 4
...
$_.FullName -replace "^.{$len}"
When having trouble, simplify:
This function will do what you are apparently trying to accomplish:
Function Remove-Parent {
param(
[string]$Path,
[string]$Parent)
$len = $Parent.length
$Path.SubString($Len)
}
The following is not the way you likely would use it but does demonstrate that the function returns the expected results:
#'
D:\this\is\where\it\starts
D:\this\is\where\it\starts\dir1\file1
D:\this\is\where\it\starts\dir1\file2
D:\this\is\where\it\starts\dir1\file3
D:\this\is\where\it\starts\dir1\dir2\file1
'# -split "`n" | ForEach-Object { Remove-Parent $_ 'D:\This\Is' }
# Outputs
\where\it\starts
\where\it\starts\dir1\file1
\where\it\starts\dir1\file2
\where\it\starts\dir1\file3
\where\it\starts\dir1\dir2\file1
Just call the function with the current path ($_.fullname) and the "prefix" you are expecting to remove.
The function above is doing this strictly on 'length' but you could easily adapt it to match the actual string with either a string replace or a regex replace.
Function Remove-Parent {
param(
[string]$Path,
[string]$Parent
)
$remove = [regex]::Escape($Parent)
$Path -replace "^$remove"
}
The output was the same as above.

Replacing String without replacing whole content of file powershell

Trying to edit this line of a file ("VoIP.Enabled "1"). I wanna change the 1 to a zero. When I change it with
$dewprefs = Get-Content .\dewrito_prefs.cfg
$dewprefs | Select-String "VoIP.Enabled" | ForEach-Object {$_ -replace "1","0"} | Set-Content .\dewrito_prefs.cfg}`
However when I use this script, it removes 100 other lines, edits the right line, then deletes everything else, just leaving the line I wanted to edit.
Any help on this matter would be highly appreciated
Select-String acts as a filter: that is, the input it is given is only passed out if it matches a pattern.
Therefore, only the line of interest is written to the output file.
Do not use Select-String if all input lines - though possibly modified - should be passed through; use only ForEach-Object, and conditionally modify each input line:
$dewprefs = Get-Content .\dewrito_prefs.cfg
$dewprefs |
ForEach-Object { if ($_ -match 'VoIP\.Enabled') { $_ -replace '1', '0' } else { $_ } } |
Set-Content .\dewrito_prefs.cfg
$_ -match 'VoIP\.Enabled' now does what Select-String did in your original command: it matches only if the input line at hand contains literal VoIP.Enabled (note how the . is escaped as \. to ensure that is treated as a literal in the context of a regular expression).
Note how both branches of the if statement produce output:
$_ -replace '1', '0' outputs the result of replacing all instances of 1 in the input line with 0
$_ simply passes the input line through as-is.
Most likely you could replace the if statement with a single -replace expression, however, and, assuming that the file is small enough to be read as a whole (quite likely, in the case of a configuration file), you can use a variant of Stu's helpful simplification.
Taking full advantage of the fact that -replace supports regexes (regular expressions), the code can update lines based on a key name such as VoIP.Enabled only, without needing to know that key's current value.
$key = 'VoIP.Enabled'
$newValue = '1'
# Construct a regex that matches the entire target line.
$regex = '^\s*' + [regex]::Escape($key) + '\b.*$'
# Build the replacement line.
$modifiedLine = "$key $newValue"
(Get-Content .\dewrito_prefs.cfg) -replace $regex, $modifiedLine | Set-Content .\dewrito_prefs.cfg
Note that writing the output back to the input file only works because the input file was read into memory as a whole, up front, due to enclosing the Get-Content call in (...).
This will work too, with PowerShell v3+, and is a little more succinct:
(Get-Content .\dewrito_prefs.cfg).replace('"VoIP.Enabled "1"', '"VoIP.Enabled "0"') |
Set-Content .\dewrito_prefs.cfg
Your quotes are a little strange (3 double quotes in total?), I've mimicked what you've asked, however.

Read numbers from multiple files and sum

I have a logfile C:\temp\data.log
It contains the following data:
totalSize = 222,6GB
totalSize = 4,2GB
totalSize = 56,2GB
My goal is to extract the numbers from the file and sum them up including the number after the comma. So far it works if I don't regex the number included with value after comma, and only use the number in front of the comma. The other problem I have is if the file only contains one row like below example, if it only contains one line it splits up the number 222 into three file containing the number 2 in three files. If the above logfile contains 2 lines or more it works and sums up as it should, as long I don't use value with comma.
totalSize = 222,6GB
Here is a bit of the code for the regex to add to end of existing variable $regex included with comma is:
[,](\d{1,})
I haven't included the above regex, as it does not sum up properly then.
The whole script is below:
#Create path variable to store contents grabbed from $log_file
$extracted_strings = "C:\temp\amount.txt"
#Create path variable to read from original file
$log_file = "C:\temp\data.log"
#Read data from file $log_file
Get-Content -Path $log_file | Select-String "(totalSize = )" | out-file $extracted_strings
#Create path variable to write only numbers to file $output_numbers
$output_numbers = "C:\temp\amountresult.log"
#Create path variable to write to file jobblog1
$joblog1_file = "C:\temp\joblog1.txt"
#Create path variable to write to file jobblog2
$joblog2_file = "C:\temp\joblog2.txt"
#Create path variable to write to file jobblog3
$joblog3_file = "C:\temp\joblog3.txt"
#Create path variable to write to file jobblog4
$joblog4_file = "C:\temp\joblog4.txt"
#Create path variable to write to file jobblog5
$joblog5_file = "C:\temp\joblog5.txt"
#Create pattern variable to read with select string
$regex = "[= ](\d{1,})"
select-string -Path $extracted_strings -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value } > $output_numbers
(Get-Content -Path $output_numbers)[0..0] -replace '\s' > $joblog1_file
(Get-Content -Path $output_numbers)[1..1] -replace '\s' > $joblog2_file
(Get-Content -Path $output_numbers)[2..2] -replace '\s' > $joblog3_file
(Get-Content -Path $output_numbers)[3..3] -replace '\s' > $joblog4_file
(Get-Content -Path $output_numbers)[4..4] -replace '\s' > $joblog5_file
$jobdata0 = (Get-Content -Path $joblog1_file)
$jobdata1 = (Get-Content -Path $joblog2_file)
$jobdata2 = (Get-Content -Path $joblog3_file)
$jobdata3 = (Get-Content -Path $joblog4_file)
$jobdata4 = (Get-Content -Path $joblog5_file)
$result = $jobdata0 + $jobdata1 + $jobdata2 + $jobdata3 + $jobdata4
$result
So my questions is:
How can I get this to work if the file C:\temp\data.log only contains one string without dividing that single number into multiple files. It should also work if it contains multiple strings, as it is now it works with multiple strings.
And how can I include the comma values in the calculation?
The result I get if I run this script should be 282, maybe its even possible to shorten the script?
Where $log_file has contents like the example above.
Get-Content $log_file | Where-Object{$_ -match "\d+(,\d+)?"} |
ForEach-Object{[double]($matches[0] -replace ",",".")} |
Measure-Object -Sum |
Select-Object -ExpandProperty sum
Match all of the lines that have numerical values with optional commas. I am assuming they could be optional as I do not know how whole numbers appear. Replace the comma with a period and cast as a double. Using measure object we sum up all the values and expand the result.
Not the only way to do it but it is simple enough to understand what is going on.
You can always wrap the above up in a loop so that you can use it for multiple files. Get-ChildItem "C:temp\" -Filter "job*" | ForEach-Object... etc.
Matt's helpful answer shows a concise and effective solution.
As for what you tried:
As for why a line with a single token such as 222,6 can result in multiple outputs in this command:
select-string -Path $extracted_strings -Pattern $regex -AllMatches |
% { $_.Matches } | % { $_.Value } > $output_numbers
Your regex, [= ](\d{1,}), does not explain the symptom, but just \d{1,} would, because that would capture 222 and 6 separately, due to -AllMatches.
[= ](\d{1,}) probably doesn't do what you want, because [= ] matches a single character that can be either a = or a space; with your sample input, this would only ever match the space before the numbers.
To match characters in sequence, simply place them next to each other: = (\d{1,})
Also note that even though you're enclosing \d{1,} in (...) to create a capture group, your later code doesn't actually use what that capture group matched; use (...) only if you need it for precedence (in which case you can even opt out of subexpression capturing with (?:...)) or if you do have a need to access what the subexpression matched.
That said, you could actually utilize a capture group here (an alternative would be to use a look-behind assertion), which allows you to both match the leading =<space> for robustness and extract only the numeric token of interest (saving you the need to trim whitespace later).
If we simplify \d{1,} to \d+ and append ,\d+ to also match the number after the comma, we get:
= (\d+,\d+)
The [System.Text.RegularExpressions.Match] instances returned by Select-String then allow us to access what the capture group captured, via the .Groups property (the following simplified example also works with multiple input lines):
> 'totalSize = 222,6GB' | Select-String '= (\d+,\d+)' | % { $_.Matches.Groups[1].Value }
222,6
On a side note: your code contains a lot of repetition that could be eliminated with arrays and pipelines; for instance:
$joblog1_file = "C:\temp\joblog1.txt"
$joblog2_file = "C:\temp\joblog2.txt"
$joblog3_file = "C:\temp\joblog3.txt"
$joblog4_file = "C:\temp\joblog4.txt"
$joblog5_file = "C:\temp\joblog5.txt"
could be replaced with (create an array of filenames, using a pipeline):
$joblog_files = 1..5 | % { "C:\temp\joblog$_.txt" }
and
$jobdata0 = (Get-Content -Path $joblog1_file)
$jobdata1 = (Get-Content -Path $joblog2_file)
$jobdata2 = (Get-Content -Path $joblog3_file)
$jobdata3 = (Get-Content -Path $joblog4_file)
$jobdata4 = (Get-Content -Path $joblog5_file)
$result = $jobdata0 + $jobdata1 + $jobdata2 + $jobdata3 + $jobdata4
could then be replaced with (pass the array of filenames to Get-Content):
$result = Get-Content $joblog_files