Trying to generate an alphanumeric hex code in powershell - powershell

I'm trying to generate an image based on a 6 char hex code (color code) that's being generated in powershell, but I'm getting an error for System.String when I add the non-digit characters, script is as follows:
# Generate random hex code for the background color
$backgroundColor = "#" + [System.String]::Join("", (Get-Random -Count 6 -InputObject (0..9 + "a".."f") | ForEach-Object { $_ }))
# Generate random hex code for the text color
$textColor = "#" + [System.String]::Join("", (Get-Random -Count 6 -InputObject (0..9 + "a".."f") | ForEach-Object { $_ }))
The error I'm receiving is this:
Cannot convert value "a" to type "System.Int32". Error: "Input string was not in a correct format."
At line:5 char:1
+ $backgroundColor = "#" + [System.String]::Join("", (Get-Random -Count ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvalidCastFromStringToInteger
When I remove the "a".."f" portion, it generates a 6 digit hex just fine, but I need to have the alphas as well for the full spectrum, not sure how to include those characters in these snippits.
Any advice would be greatly appreciated.
I've really only tried removing the alphas to test the rest of the script which works fine, but only generates a code with the integers 0-9 which works, but isn't desired.

Use [char[]]"0123456789ABCDEF" instead of 0..9 + "a".."f".
$backgroundColor = "#" + [System.String]::Join("", (Get-Random -Count 6 -InputObject ([char[]]"0123456789ABCDEF") | ForEach-Object { $_ }))
$textColor = "#" + [System.String]::Join("", (Get-Random -Count 6 -InputObject ([char[]]"0123456789ABCDEF") | ForEach-Object { $_ }))
UPDATE: Shorter version.
$backgroundColor = '#'+ (([char[]]"0123456789ABCDEF" | Get-Random -Count 6) -join '')
$textColor = '#'+ (([char[]]"0123456789ABCDEF" | Get-Random -Count 6) -join '')

Your code as is will succeed in PowerShell Core, however isn't compatible with Windows PowerShell. Main reason is, the range operator .. on characters (i.e.: 'a'..'f') became a thing in PowerShell v6:
To overcome this you can change your logic a bit so it is compatible with both versions:
$map = [char] '0'..[char] '9' + [char] 'a'..[char] 'f'
'#' + [string]::new((Get-Random -Count 6 -InputObject $map))

To work in powershell 5.1 with the [char[]] cast. I don't think you need the foreach-object.
[int][char]'a'
97
[int][char]'f'
102
"#" + [System.String]::Join("", (Get-Random -Count 6 -InputObject (0..9 +
[char[]](97..102))))
#d1b5a8
Or
"#" + -join (0..9 + [char[]]([char]'a'..[char]'f') | Get-Random -Count 6)

Related

Passing arguments to Call operator (&) as a string in PowerShell

I have a PowerShell script where I use the Call operator to run mkvmerge.exe with a set of arguments/options. I wanted to collect the options in a string so that the string could be expanded based on inputs earlier in the script, but when the destination file is in this string mkvmerge does not recognize it for some reason. I am unsure if this is because of variable expansion or what the issue is
The code below illustrates the problem. I have also tried adding the options to an array and using the # operator, but with no luck
$mkvmerge = "C:\Program Files\MKVToolNix\mkvmerge.exe"
$applyoptions = $true
$subtitleformat = "srt"
$isolanguage = "eng"
foreach ($file in Get-ChildItem -File -Include *.mkv) {
<# Make the string with options for mkvmerge #>
$mkvmergeparams = ""
if ($applyoptions -eq $true) {
$mkvmergeinstructions += ('`' + "#options.json")
}
$mkvmergeparams += ("-o " + "`"" + $file.DirectoryName + "\Remux-" + $file.BaseName + ".mkv`" " + "`"" + $file + "`"")
if (-not($subtitleformat -eq "")) {
$mkvmergeinstructions += (" --default-track " + $subtitledefault) + " --language " + "0:" + $isolanguage + " " + "`"" + $file.DirectoryName + "\" + $file.BaseName + "." + $subtitleformat + "`""
}
<# Check the string #>
Write-Host $mkvmergeparams
<# This does not work, but I would like it to #>
& $mkvmerge $mkvmergeparams
<# This works, but would require a separate line for each set of possible options #>
#& $mkvmerge `#options.json -o ($file.DirectoryName + "\" + "Remux-" + $file.BaseName + ".mkv") $file --default-track $subtitledefault --language ("0:" + $isolanguage) ($file.DirectoryName + "\" + $file.BaseName + "." + $subtitleformat)
}
Read-Host -Prompt "Press enter to exit"
In the second example the variables are expanded and mkvmerge recognizes the options given, but this requires having a separate mkvmerge line for each set of possible inputs
When checking the string it seems to expand to look exactly as it should
Found the answer. I had to create an array with the arguments
<# Build array with options for mkvmerge #>
$mkvmergeparams = $null
if ($applyoptions -eq $true) {$mkvmergeparams += #('#options.json')}
$mkvmergeparams += #("-o", ($file.DirectoryName + "\Remux-" + $file.BaseName + ".mkv"), $file)
if (-not($subtitleformat -eq "")) {$mkvmergeparams += #("--default-track", $subtitledefault, "--language", ("0:" + $isolanguage), ($file.DirectoryName + "\" + $file.BaseName + "." + $subtitleformat))}

How do I add multi-line strings to an array in powershell?

I'm trying to loop through a series of txt files extracting information into arrays based a : delimiter.
All values i'm taking from the text files fit on a single line, except for one. (The "ad text" value). This cuts off the information after line 1 in the final output. When I remove line carriages and breaks beforehand, none of the fields are inputted correctly.
How would I specify wanting my array to accept multi-line inputs for the "ad text" field?
Below is the code i'm working with:
$files = ls "*.txt"
$dictionary = #{}
[System.Collections.Generic.List[String]]$list = #()
foreach($f in $files){$in = Get-Content $f
$in.Split([Environment]::NewLine) | ForEach-Object { $key,$value = $_.Split(':')
$dictionary[$key] = $value
}
[void]$list.Add( $dictionary['Ad ID'] + ',' + $dictionary['Ad Text'] + ',' + $dictionary['Ad Landing Page'] + ',' + $dictionary['Ad Targeting Location'] + ',' + $dictionary['Age'] + ',' + $dictionary['Language'] + ',' + $dictionary['Placements'] + ',' + $dictionary['Interests'] + ',' + $dictionary['Behaviors'] + ',' + $dictionary['Ad Impressions'] + ',' + $dictionary['Ad Clicks'] + ',' + $dictionary['Ad Spend'] + ',' + $dictionary['Ad Creation Date'] + ','+ $dictionary['Friends'] + ',' + $dictionary['Ad End Date'] + ',' + $dictionary['Excluded Connections'] + ',' + $dictionary['Image'] + ',' + $dictionary['Ad Targeting Location']+','+$dictionary[‘File Name’] )
}
$list | Out-File -FilePath '.\trial.csv' -Append
Assuming that the additional lines following Ad Text:-prefixed lines do not contain : chars themselves:
# Create sample text file t.txt
#'
Ad ID:10
Ad Text:one
two
Ad Landing Page:http://example.org
'# > t.txt
# Split the lines into field names and values by ":" and
# build a dictionary.
$dict = [ordered] #{}
$i = 0
(Get-Content -Raw t.txt) -split '(?m)^([^\n:]+):' -ne '' | ForEach-Object {
if ($i++ % 2 -eq 0) {
$key = $_
} else {
$dict[$key] = $_
}
}
# Output the dictionary, showing each entry as a list.
$dict | Format-List
The output is as follows, showing that the Ad Text entry comprises two lines:
Name : Ad ID
Value : 10
Name : Ad Landing Page
Value : http://example.org
Name : Ad Text
Value : one
two

Powershell compare dates and enable AD User if disabled and in range

I can't seem to figure out why this script won't work. There are no errors, but the $User object isn't getting evaluated to $True. The object is to enable a re-hired user's AD account if another system shows the re-hire date to be less than or equal to 8 days out from today.
$CSVLine = "C:\scripts\adp\Test ADP Import spec.csv"
$ErrorLog = "C:\scripts\adp\ADPProcessErrors.csv"
$a = Get-Date
ForEach ($row in (import-csv $CSVLine)) {
$User = Get-ADUser -LDAPFilter ("(sAMAccountName=" + $Row.sAMAccountName + ")") -Properties *
#Write-Host ("User:" + $user.samaccountname + " enabled =" + $user.enabled + " ")
If ((($User.Enabled -eq $False)) -and ($Row.'Date of hire/rehire' -gt $a) -and (($Row.'Date of hire/rehire') -le ($a.AddDays(8))) ) {
(Set-ADUser -Enabled $True -Identity $User)
("SID:" + $Row.sAMAccountName + ", User:[" + $User.givenName + " " + $User.sn + "] Re-Hire date is in range. Account enabled.") | Out-File -FilePath $ErrorLog -Append
}
}
Write-Host ("CSV Object: " + $Row.'Date of hire/rehire'.GetType())
Write-Host ("CSV Object Value:" + $Row.'Date of hire/rehire' + " " )
Write-Host ("User:" + $user.samaccountname + " enabled =" + $user.enabled + " ")
Those dates might need to be casted as [datetime]/dates so that the math against $a would work. It is most likely treating those as string to just doing alphabetical comparison.
PS C:\Users\mcameron> "b" -gt "a"
True
Can't tell you exactly what to do without sample dates so we can see the formats. From the comment if you dates are formatted like "2/8/2015" then a simple cast would address this.
PS C:\Users\mcameron> [datetime]"2/8/2015"
Sunday, February 08, 2015 12:00:00 AM
Update your if to the following:
If ((($User.Enabled -eq $False)) -and ([datetime]($Row.'Date of hire/rehire') -gt $a) -and ([datetime]($Row.'Date of hire/rehire') -le ($a.AddDays(8))) ){
#.....Process
}
It's a bit difficult to say without a sample of the input file but, if you're getting no error, I'd start by checking if $Row.sAMAccountName is evaluating to anything inside the loop. Like you have with your commented-out Write-Host line, for the $user properties.
Cheers.

Reading and increasing a number in a textfile with ++ or equivalent

I am trying to read a number from a file with Get-Content and adding it to a variable.
Then i add this number to a string in a file, increase the number by 1, then save that to the file again.
I have tried something like:
$i = Get-Content C:\number.txt
$i++
Set-Content C:\number.txt
The content of number.txt is: 1000
But i get this error:
The '++' operator works only on numbers. The operand is a 'System.String'.
At line:2 char:5
+ $i++ <<<<
+ CategoryInfo : InvalidOperation: (1000:String) [], RuntimeException
+ FullyQualifiedErrorId : OperatorRequiresNumber
Does anyone have any idea of a better way of doing this operation?
I guess you need to convert it to an integer before increment it.
$str = Get-Content C:\number.txt
$i = [System.Decimal]::Parse($str)
$i++
Set-Content C:\number.txt $i
short way:
[decimal]$i = Get-Content C:\number.txt # can be just [int] if content is always integer
$i++
Set-Content C:\number.txt $i
Let's do it in one line:
[decimal] ( $i = Get-Content C:\number.txt ) | % {Set-Content C:\number.txt -value ($_ + 1); return ($_ + 1)}
Returns the value incremented. $i has the value before the increment.

How to total Kbytes of several files

I'm looping through a directory full of files and need to get the grand total of all the files in a directory. However I'm getting a conversion problem due to the way I fetch/calculate the kilobytes.
Here is my code:
$destinationDir = "\\server\folder"
$filename = "FILE_1234567.xls"
$filesize = 0
$totalfilesize = 0
$filesize = Get-ChildItem ($destinationDir + "\" + $filename) | Select-Object #{Name="Kbytes";Expression={"{0:N0}" -f ($_.Length / 1Kb)}}
$totalfilesize += [int]($filesize)
Write-Host $filesize
Write-Host $totalfilesize
However when I run this I get the following error message:
Cannot convert the "#{Kbytes=93}" value of type "Selected.System.IO.FileInfo" to type "System.Int32".
At C:\Sample.ps1:7 char:18
+ $totalfilesize += <<<< $filesize
+ CategoryInfo : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorId : RuntimeException
I'm pretty new to Powershell but it seems like I cannot cast $filesize to an integer for totaling. Can anyone help with where I've gone wrong?
Direct answer:
Ok, first I'll just answer your question about why your code is getting that error. Basically, Select-Object is returning an object which has a KBytes property - that object is what you're trying to add to $totalfilesize. There are two ways around this:
One option is to use a foreach to emit the value that you want.
$filesize = Get-ChildItem ($destinationDir + "\" + $filename) | ForEach-Object { $_.Length / 1kb }
Try this, and you'll see the result is an int, because it's only returning $_.Length, which is an int.
Alternatvely, you could add the .KBytes property instead of adding the result of Select-Object:
$totalfilesize += [int]($filesize.KBytes)
Caveat
Are you planning to iterate over multiple files? If so, then you may run into issues because in that case you'll get an array back. You can avoid that by moving the addition into a loop based on the get-childitem results:
$destinationDir = "$pshome"
$filename = "*"
$totalfilesize = 0
$filesize = Get-ChildItem ($destinationDir + "\" + $filename) | ForEach-Object { $totalfilesize += $_.Length / 1kb }
Write-Host $totalfilesize
Finally, there's the Measure-Object cmdlet which does stuff like this for free:
[int]((Get-ChildItem $PSHOME | Measure-Object -Sum Length).Sum / 1kb)
Hope that helps