Are there good references for moving from Perl to Powershell? - perl

I hate to say it, but powershell is really annoying me. I just cannot seem to get my mind around it. I have an O'Reilly book on the topic, and I can see how to do some extremely powerful stuff -- it's the easy stuff I can't seem to get right.
Case in point: Iterate across a list of filenames.
In CMD:
for /F %x in ('dir EXPRESSION') do #(
arbitrary-action %x
)
In Perl:
#foo=glob("*");
foreach (#foo)
{
arbitrary-command $_ ;
}
In Powershell:
I'm dumbfounded. I can't seem to get it right.
But I am not sending this post so somebody can tell me the answer. I don't want the answer to this simple question. I want to know how to figure it out, and Google/Bing searches are just not cutting it. The get-help functionality is powershell is nice, but it's still not enough.
I've been programming for 20 years.
I've learned BASIC, Fortran, C, Pascal, Perl, C++, C#, BASH and CMD scripting...And never have I had the trouble I'm having with Powershell.
Are there no references "out there" for migrating from Perl to Powershell? It seems like such a straightforward thing to publish, but I have yet to find one.
Any help would be appreciated. Thanks.
Update:
Okay, so maybe this wasn't the best example to post.
I think I was thrown off by the fact that when I tried gci interactively, I got a directory listing, where what I wanted was an array of strings.
I took a leap of faith and tried:
foreach ($foo in gci "*") {
echo $foo;
}
And yeah, it worked. And yes, I can continue to do searches to piece my way through. I guess I was just hoping to find a guide that makes use of the similarity to languages I already know. I know that Microsoft published a VBScript-to-Powershell guide, so I was hoping for a Perl equivalent.
Thanks again

I've never seen such a guide. I've seen something to help people going from VBScript to PowerShell.
Bruce Payette's PowerShell in Action does have a few pages on PowerShell vs X scripting language, but that won't cut it for a conversion guide.
Now, there was a site out there that had all kinds of constructs in multiple languages, thus providing a task, and then going about solving it in all kinds of languages based on answers from the community... Anyone know what I'm talking about?

I don't know of any good Perl to Powershell comparisons but I can answer your secondary question.
$files = get-childitem "c:\test\" -filter *.dll
foreach ($file in $files) { $file.Name }
There are two different ways you can express a foreach loop in Powershell.
You can pipe an array of objects into foreach and $_ becomes the current object on each iteration.
0,1,2,3 | foreach { $_ }
Alternatively, you can pass a variable to iterate over.
foreach ($num in 0,1,2,3) { $num }
Output in both cases.
0
1
2
3

Like he said himself, he realized the potential to get some very powerful things using powershell...I recently started using it myself and the ease with which things can be done is astounding...just a few lines is all it takes to accomplish things that in python woulda taken me some extra workarounds etc.

I'm curious do you have the powershell cookbook? I thought since I had programming experience that it would be the best way to quickly learn powershell. This turned out to be a poor assumption. Even though the book was really good, I was struggling because I needed a book that was structured more for learning than reference.
The two best free online ebooks I found are:
https://blogs.technet.com/chitpro-de/archive/2007/05/10/english-version-of-windows-powershell-course-book-available-for-download.aspx
http://powershell.com/cs/blogs/ebook/
I'm still looking for a good print book.

I think finding a Perl to PowerShell guide is going to be difficult. It is actually more accurate to compare PowerShell to BASh and the C Shell than Perl. I think what makes learning PowerShell difficult from most of the available books is that they are aimed at system admins, not programmers. I recommend the "PowerShell in Action" for the best coverage of PowerShell as a general purpose programming language.
The other thing you need to do is immediately embrace the core principal of PowerShell -- you are dealing with objects, not text. With BASh and the other Unix shells, it's all about text manipulation and while Perl can do objects, its roots are still very much in the Unix shell and command line utilities (grep, sed, awk, etc.).
Larry Wall stole a lot of great ideas from other languages when he created Perl. PowerShell has done the same, using the POSIX shell as its starting point. You can see a lot of Perl in PowerShell, as well as other languages. I think the best way to learn PowerShell is by having a PowerShell window in front of you while reading "PowerShell in Action" which will help you get into the PowerShell way of thinking about how it does objects. It is easy to interactively enter code snippets in a PowerShell window and examine the properties and methods available within the objects returned by the commands.
BTW -- if you are use to using BASh with the default command line editing features, put the following command in your PowerShell $PROFILE
Set-PSReadlineOption -editmode Emacs
Do this and you'll feel right at home. Now that I can run PowerShell on Linux and the Mac, I'm not sure what I will ever need BASh for again.

Funny... I had the same issues when I started with PowerShell, but after using PowerShell now for a couple of months, I have dumped Perl like ugly Sally after the Prom. There are several great ways of using foreach to loop through an list of objects. Let's look at the following example where I have a number of services that I want to make sure are running on my Windows Server (the processes all have Equal in the name). I can do this with a one liner command as follows:
Get-Service | where-object {$_.displayname -like "Equal*"} | foreach {
if($_.Status -eq "Stopped") {
Write-Host "`nRestarting..."
write-host $_.DisplayName
Start-Service $_.name
}
}
The first part of the command is the get-service command - filtering the services looking for services with the display name with Equal in them. The next part of the one liner really shows the beauty of PowerShell. The get-service command returns a list of objects, which can then be acted upon by piping them into a foreach loop. Note I did not have to declare any variables or an array to hold the objects. The objects are stored in the default $_ variable from which I can pull out object properties like name and status. Foreach returned object (service) I check its status to see if it's status is stopped and if it is it is restarted with the Start-Service command. I could do this a similar action with Perl, but I would have to screen scrape and place the values into an array, then foreach over the array in a similar manner as I did above. The screen scrapping would involve grep with regexp which adds lines of code. Because PowerShell commands produce objects with properties, I can quickly drill down to the properties I am looking for without the screen scraping hassles.

Related

Get all references to a given PowerShell module

Is there a way to find a list of script files that reference a given module (.psm1)? In other words, get all files that, in the script code, use at least 1 of the cmdlets defined in the module.
Obviously because of PowerShell 3.0 and above, most of my script files don't have an explicit Import-Module MODULE_NAME in the code somewhere, so I can't use that text to search on.
I know I can use Get-ChildItem -Path '...' -Recurse | Select-String 'TextToSearchFor' to search for a particular string inside of files, but that's not the same as searching for any reference to any cmdlet of a module. I could do a search for every single cmdlet in my module, but I was wondering if there is a better way.
Clarification: I'm only looking inside of a controlled environment where I have all the scripts in one file location.
Depending on the scenario, the callstack could be interesting to play around with. In that case you need to modify the functions which you want to find out about to gather information about the callstack at runtime and log it somewhere. Over time you might have enough logs to make some good assumptions.
function yourfunction {
$stack = Get-PSCallStack
if ($stack.Count -gt 1) {
$stack[1] # log this to a file or whatever you need
}
}
This might not work at all in your scenario, but I thought I throw it in there as an option.

Microsoft's Consistency in PowerShell CmdLet Parameter Naming

Let's say I wrote a PowerShell script that includes this commmand:
Get-ChildItem -Recurse
But instead I wrote:
Get-ChildItem -Re
To save time. After some time passed and I upgraded my PowerShell version, Microsoft decided to add a parameter to Get-ChildItem called "-Return", that for example returns True or False depending if any items are found or not.
In that virtual scenario, do I have I to edit all my former scripts to ensure that the script will function as expected? I understand Microsoft's attempt to save my typing time, but this is my concern and therefore I will probably always try to write the complete parameter name.
Unless of course you know something I don't. Thank you for your insight!
This sounds more like a rant than a question, but to answer:
In that virtual scenario, do I have I to edit all my former scripts to ensure that the script will function as expected?
Yes!
You should always use the full parameter names in scripts (or any other snippet of reusable code).
Automatic resolution of partial parameter names, aliases and other shortcuts are great for convenience when using PowerShell interactively. It lets us fire up powershell.exe and do:
ls -re *.ps1|% FullName
when we want to find the path to all scripts in the profile. Great for exploration!
But if I were to incorporate that functionality into a script I would do:
Get-ChildItem -Path $Home -Filter *.ps1 -Recurse |Select-Object -ExpandProperty FullName
not just for the reasons you mentioned, but also for consistency and readability - if a colleague of mine comes along and maybe isn't familiar with the shortcuts I'm using, he'll still be able to discern the meaning and expected output from the pipeline.
Note: There are currently three open issues on GitHub to add warning rules for this in PSScriptAnalyzer - I'm sure the project maintainers would love a hand with this :-)

Finding the index of a key and value within an array using a wildcard in Powershell

I have been working on an issue and I was able to get done what I need to get done in a relatively decent and acceptable way but I am curious as to why I ran into some of the problems I did.
I'm not too savvy with PowerShell or C# but I have some experience with Java, C++, and a few others. So, if I overlook something really simple, you'll have to forgive me. I'm not looking for a critique of my solution, just some insight into some of the blockades I came across.
What I needed to do was use Powershell to query an LDAP setting. I needed to know MaxConnIdleTime and I needed that either assigned to a variable or accessible through a subroutine [sic] (ex $ldapPolicies.MaxConnIdleTime) so that I could run it through a conditional statement.
Here is how I accomplished it:
$ldap = Get-ADObject -SearchBase "CN=Query-Policies,CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,DC=$ENV:COMPUTERNAME,DC=$dc" -Filter 'ObjectClass -like "queryPolicy"' -Properties ldapadminlimits
$ldap = #($ldap.ldapadminlimits)
$ldap | %{
if($_.startswith("MaxConnIdleTime")) {
$match = $_
}
}
I tried NTDSUtil.exe, however, I couldn't redirect the "Show Values" to a text file to read from later and I couldn't write it to a variable in PowerShell.
I tried Start-Transcript and then ran NTDSUtil but it only recorded what occurred within PowerShell and not what happened in NTDSUtil.
Also, I tried giving all of the commands to NTDSUtil at once (NTDSUtil "ldap policies" "connections" "connect to server $ENV:COMPUTERNAME" q "Show Values") but PowerShell doesn't show anything in the console and I have press the exit sequence to return back to PS>.
I know that I could use LDP but I'm not too familiar with ADSI. Research appeared to say that going about attempting to get an LDPdump is a bit antiquated and I pretty much abandoned that attempt.
One of the issues that I had that caused me a small bit of frustration (and the reason I am asking this question) is why can I not search an array and find the index of an item using a wildcard? I tried doing this:
$ldap.IndexOf("MaxConnIdleTime*")
AND
$ldap.IndexOf($ldap -like "MaxConnIdleTime*")
but it always returned -1.
It would work correctly if I tried:
$ldap.IndexOf("MaxConnIdleTime=100")
given that the value was indeed 100. But I am validating that the value was correct.
I know that I could just do something like this:
if($ldap -contains "MaxConnIdleTime=100") {
DO SOMETHING...
} else {
DO SOMETHING ELSE...
}
Why is it that I can't search an array using a wildcard operator? There was no ambiguity, so, it should have worked, right?
I'm not looking for a critique of how I accomplished this, I'm just wanting to understand why it behaved like it did.
Thanks.
I don't think there's a straightforward "search an array by wildcard and return an index" cmdlet, method, statement, etc. in PowerShell.
.IndexOf is not designed to work with a wildcard.
When you used the -like operator on the array, you likely found only a single matching object, but -like returns an array of matches when used on an array.
Passing the array into .IndexOf() then looks for an array element that is itself an array, even if that array only has one object.
This would work:
$ldap.IndexOf(($ldap -like "MaxConnIdleTime*")[0])
As long as you always wanted to find the first one.

preplog.exe ran in foreach log file

I have a folder with x amount of web log files and I need to prep them for bulk import to SQL
for that I have to run preplog.exe into each one of them.
I want to create a Power script to do this for me, the problem that I'm having is that preplog.exe has to be run in CMD and I need to enter the input path and the output path.
For Example:
D:>preplog c:\blah.log > out.log
I've been playing with Foreach but I haven't have any luck.
Any pointers will be much appreciated
I would guess...
Get-ChildItem "C:\Folder\MyLogFiles" | Foreach-Object { preplog $_.FullName | Out-File "preplog.log" -Append }
FYI it is good practice on this site to post your not working code so at least we have some context. Here I assume you're logging to the current directory into one file.
Additionally you've said you need to run in CMD but you've tagged PowerShell - it pays to be specific. I've assumed PowerShell because it's a LOT easier to script.
I've also had to assume that the folder contains ONLY your log files, otherwise you will need to include a Where statement to filter the items.
In short I've made a lot of assumptions that means this may not be an accurate answer, so keep all this in mind for your next question =)

powershell get PID for specific app running for calling user

We have an ERP application with restrictive licensing, which we access via RemoteApp. We need to only allow one instance per user. So, I've been experimenting with PowerShell to try to do this.
What the script has to do is check and see if "pxxi.exe" is running for the calling user.
My first attempt used WMI. It worked, but it turns out WMI is very very slow.
(gwmi win32_process -Filter "name='pxxi.exe'" | ? {$_.getowner().user
-eq $env:username}).pid
My second attempt used Get-Process. This works great, but unfortunately requires elevated rights.
Get-Process pxxi -IncludeUserName | ? {$_.username -match $env:username}).id
My third attempt focused on the win32 command line program Tasklist.
$result = Invoke-Command { c:\windows\system32\tasklist.exe /fi
"username eq $env:username" /fi "imagename eq pxxi.exe"}
This works! And thanks to EBGreen's code, I can extract just the PID.
($result[3] -split '\s+')[1]
And while that runs really quick as an Administrator, for regular users, Tasklist runs as slow as WMI, above...
So, that puts me back to square one.
Does anyone out there know of a bit of code to quickly check and see if a user XYZ is running a process with name ABC.EXE ?? This needs to work for regular users (but regular users don't need to see other user's processes) and it needs to not take 30+ seconds to run.
I'm not married to powershell. VBScript would be fine. Or any little tool available on the internet.
Thanks!
For the example that you have:
($result[3] -split '\s+')[1]
Be aware that this works for just one result being returned. If you expect more than one instance then you should write a loop to iterate from the 4th item in the array on splitting each item to get that process's PID.
I gave up trying to find a way to do it in Powershell. Either the method was too slow, or required admin.
So I wrote something in C#:
c# - check if program is running for current user