Powershell loop that counts down - powershell

I'm trying to print a warning to let the user know that 0 isn't a valid option for an assignment for class. Just started learning about powershell and can't find any help on how to set a parameter like this.
Assignment is 3.) Write a loop that calculates $num^2 for each iteration and prints the value using Write-Host. This loop must execute at least once and beginning counting down. When $num = 0, the loop should end. Prevent this loop from receiving $num as 0 as this will cause an infinite loop. If 0 is set as $num, print a warning to let the user know that 0 isn’t a valid option.
So far this is what I have
$num=Read-Host -Prompt 'Enter a number'
do
{
write-host $num
$num--
}
until($num -eq -1)
Any help would be greatly appreciated

There are two fundamental problems:
Read-Host always returns a string, and --, the decrement operator, can only be used on numbers; you must convert the string to a number first:
[int] $num = Read-Host 'Enter a number'
write-host $num $num-- doesn't work as expected: while the $num references are expanded, the trailing -- is considered a string literal, not the decrement operator; to treat $num-- as an expression, you must enclose it in (...); also, to print the already decremented number, reverse the operands:
$num = 42; Write-Host ($num--) $num yields 42 41, as intended.
Incorrect: $num = 42; Write-Host $num $num-- yields 42 42--
The - complex - rules PowerShell uses when parsing unquoted command arguments are summarized in this answer

Try this:
$num = Read-Host -Prompt 'Enter a number'
$num = [int]::Parse( $num )
do
{
write-host "$num square is $([Math]::Pow($num,2))"
}
while(--$num)

Related

Powershell -replace for perl users: how to replace-match-$1 in PowerShell?

Take a look at this perl code:
$x = "this is a test 123 ... this is only a test"
$x =~ s/"test\s+(\d+)"/"test $1"/
print $x
this is a test 123 ... this is only a test
Notice that I match a number with regex (\d+), it gets put into the temporary variable $1, then it gets put in the output string as an expansion of $1 temporary variable...
Is there a way to do above perl replacement in powershell? I'm thinking if its possible then its something like this??
$x = "this is a test 123 ... this is only a test"
$x = $x -replace "test\s+(\d+)", "test $Matches[1]"
write-host $x
this is a test 123 ... this is only a test
Of course it doesn't work... I was curious how to do this since i have a lot of perl scripts to convert to PowerShell..
Not that different in PowerShell:
$x = "this is a test 123 ... this is only a test"
$x = $x -replace 'test\s+(\d+)', 'test $1'
Write-Host $x
Output:
this is a test 123 ... this is only a test
Regex details:
test Match the characters “test” literally
\s Match a single character that is a “whitespace character” (spaces, tabs, line breaks, etc.)
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
( Match the regular expression below and capture its match into backreference number 1
\d Match a single digit 0..9
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
)
You can find out more here
There's another way in powershell 7 with script blocks { }. You also need the subexpression operator $( ) to refer to object properties or arrays inside a string. I'm just saving $_ to $a so you can look at it. It's a little more convoluted, but sometimes the timing of '$1' (note the single quotes) isn't what you need, like if you want to add to it.
"this is a test 123 ... this is only a test" -replace "test\s+(\d+)",
{ $a = $_ ; "test $(1 + $_.groups[1].value)" } # 1 + 123
this is a test 124 ... this is only a test
$a
Groups : {0, 1}
Success : True
Name : 0
Captures : {0}
Index : 10
Length : 13
Value : test 123
ValueSpan :

Concatenate Arrays as String with PowerShell

i have two array ($a , $b) which holds the following strings:
$a
MSSQL11.SQLINST15
MSSQL12.SQLINST16
MSSQL12.SQLINST17
$b
2874
2884
2885
That i'm trying to concatenate to a new array ($c), as a strings with a comma sign in the middle:
$c
MSSQL11.SQLINST15,2874
MSSQL12.SQLINST16,2884
MSSQL12.SQLINST17,2885
Problem is, that using my current ps code:
$c = #($a + ',' + $b)
[string]::Concat($c)
$c
I'm getting a very strange output:
MSSQL11.SQLINST15
MSSQL12.SQLINST16
MSSQL12.SQLINST17
,
2887
2884
2885
At the end, i've use a powershell hashtable.
I like this way of doing it. Create a zip function to combine your two arrays into a single array of tuples.
Then it's just a matter of piping piece-wise over that tuple list our zip function gives us. Super convenient and reusable.
function Zip($a, $b) {
while ($a) {
$x, $a = $a
$y, $b = $b
[tuple]::Create($x, $y)
}
}
$c = zip $a $b |% {$_.item1 + [string]$_.item2}
Note you can choose where ya wanna to your [string] conversion (in the following, the last line of zip).
function Zip($a, $b) {
while ($a) {
$x, $a = $a
$y, $b = $b
[tuple]::Create([string]$x, $y)
}
}
$c = zip $a $b |% {$_.item1 + $_.item2}
Extra info: Looks like you're accidentally just combining the two arrays using a ",". But $a and $b are arrays, so what you actually want to do is combine the first element of $a with the first element of $b (aka $a[0] + [string]$b[0], and so on for each element. Why the [0]? Remember we almost always start counting at 0 in programming. So the second item of an array is actually [1].
Edit: Here is an example using a foreach loop.
```
foreach($item in $a) {
$i = $a.IndexOf($item)
$a[$i] + [string]$b[$i]
}
```
If for some reason $a has 10 things in it, and $b only has 2 things, it's gonna give you funny behavior. So be careful there. Please let me know if I can better clarify anything. Don't forget to experiment.

powershell double for loop

Need advice on loop
$Variable contains 11111 22222
foreach ($variable in $value) {
for ([byte]$c = [char]'b'; $c -le [char]'c'; $c++) {
$variable."([char]$c)" } }
I am looking output as 11111b and then 22222c but currently, I am getting 11111b , 11111c and then 22222b and then 22222c.
Kindly advice
I am assuming you mean that $value, not $variable, contains 11111 and 22222, specifically in an array.
Since you want $c to maintain its value between iterations of the foreach loop you need to initialize $c outside of the foreach loop. Therefore, you really don't need (or, rather, should not use) two loops at all.
$value = 11111, 22222;
[Byte] $c = [Char] 'b';
foreach ($variable in $value)
{
"$variable$([Char] $c++)"
}
This gives the output you are seeking:
11111b
22222c

another line split (powershell or other scripting tools under windows)

i have a log file in hand, looks like this:
0226 111641 (1911) 0 some space separated message containing whatever letters and marks
I need to import it to database, to use filters on it, when troubleshooting is needed. Currently i think powershell is best selection to achieve this, but i'm too green to know specifically how to do it so it can perform actually. I tried to do it like this:
$file = Get-Content "test.txt"
foreach ($line in $file)
{
#Write-Host $line
$a = $line
$month1 = $a[0..1]
$month2 = "$month1"
$month2 = $month2.ToString()
$month = $month2.Replace(" ", "")
$day1 = $a[2..3]
$day2 = "$day1"
$day2 = $day2.ToString()
$day = $day2.Replace(" ", "")
}
... and so on. after that inserting it to database. However, log file is quite big (currently 15MB in 3 weeks, expected to be hundreds of megabytes within months), and already the script takes about 4-5min to process it.
So what i need is method to split four space separated columns from beginning of the line, convert first and second to date and time and add them with message part of the line to database. Separately processing each block of text seems too time consuming, excel for example can process this file within seconds. Is there around some position aware csv-import command?
Thanks.
Found this:
Replace first two whitespace occurrences with a comma using sed
would help, if i would use linux... :(
I'm not sure if the ConvertFrom-Csv or Import-Csv cmdlets can help you since your field delimiter can appear in the message field. Without knowing what these different fields are, I came up with this:
$file = Get-Content "test.txt"
foreach ($line in $file)
{
# Split $line into at most 5 fields
$fields = $line -split ' ', 5;
# fields[0] is a two-digit month followed by a two-digit day
$date = [DateTime]::ParseExact($fields[0], 'MMdd', $null);
$field2 = $fields[1];
$field3 = $fields[2];
$field4 = $fields[3];
$message = $fields[4];
# Process variables here...
}
Using the sample text you provided for $line, the above variables look like this after execution:
PS> Get-Variable -Name #('date', 'field*', 'line', 'message')
Name Value
---- -----
date 2/26/2012 12:00:00 AM
field2 111641
field3 (1911)
field4 0
fields {0226, 111641, (1911), 0...}
line 0226 111641 (1911) 0 some space separated message
message some space separated message
More information will be needed on the format of your data in order to give you a more specific answer.

How do I check for an empty scalar in Perl?

How do I check for an empty scalar in perl? If I have no $list, I do not want to send an email.
Can I check for empty message in the send_email routine or do this outside?
I have a query that uses Win32::OLE.
my $servSet = $wmiObj->ExecQuery("SELECT * FROM Win32_Service WHERE DisplayName LIKE 'ServiceNameHere%'", "WQL", wbemFlagReturnImmediately | wbemFlagForwardOnly);
I'm looping through it here and building a list $list
foreach my $serv (in $servSet) {
next if $serv->{started};
my $sname = $serv->{name};
my $sstate = $serv->{started};
my $ssmode = $serv->{startmode};
$list .= "Service: $sname - $sstate - $ssmode\n";
}
I use the $list to send as body of the email:
sub send_email {
...
..
$smtp->datasend($list);
..
.
}
In Perl, undef, "" (and also 0 and "0") evaluate to "false". So you can just do a boolean test:
send_email() if $list;
I don't like to fool around with what's actually in the variable. If I want to see if anything, anything at all, is in a scalar, I check its length:
send_mail() if length $scalar;
Have you tried this?
if (!($list eq ""))
send_email(...);
or
if ($list ne "")
send_email(...);