DEV Community

Cover image for A Linux Geek Tried PowerShell — It Feels *Power*ful Indeed
deathroll
deathroll

Posted on • Updated on

A Linux Geek Tried PowerShell — It Feels *Power*ful Indeed

This article represents my personal opinion and is meant mostly for Linux (and other Unix-like) users, but feel free to join anyways ;)

Update 2023-05-17: Changed the parameters examples formatting.

Table of Contents

Preamble

I'm on the finish line to get a college degree as a Network and System Administrator. Even though I use Unix-like operating systems for more than five years and want to specialize in them — especially those based on the Linux kernel — the enterprise still runs on Windows partially. That's why even we, the Unix(-like) enthusiasts, still may have to learn it to some extent.

Less than a week ago, I was preparing for one of the last exams of the last semester. As a part of an assignment, an administrator is to create multiple AD OUs and populate them with a defined number of users. Each user is a member of a security group with a name similar to the OU's. Since the number of users for each OU is somewhat huge, up to 30, the most productive way to create them is via a script. A PowerShell script. It takes less than five minutes to write such a script but can save a lot of time and prevent headaches in the future. In a real-world scenario, this script could be placed in a version control system repository.

While I was writing the script, I learned a bit about PowerShell and liked how some things are approached. The Unix-like systems' communities would benefit from borrowing some techniques or even using the PowerShell itself, I think. Don't get me wrong, I know who's behind this project, and not trying to convince anyone to abandon their beloved Unix shells ;)

The Good Stuff

Working With Types

Oh boy, it is so satisfying when a shell enables you to work with types like most of the modern programming languages do. Type casting? Check. Type-specific auto-completion? Here you go. Explicit type declarations? Check.

While using this shell, one can nearly stop worrying about weird behavior when working with data of mixed types. For example, collections like arrays and lists may contain objects of one specific type or multiple types.

Unambiguous Types
A String is a String, a Double is a Double, and so on...
PS /home/deathroll> [Math]::PI.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Double                                   System.ValueType

PS /home/deathroll> $(665).GetType() 

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Int32                                    System.ValueType

PS /home/deathroll>
Enter fullscreen mode Exit fullscreen mode

Type Casting
PS /home/deathroll> $([Int] 14.15)          
14
PS /home/deathroll> $([String] 14).GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

PS /home/deathroll>
Enter fullscreen mode Exit fullscreen mode

Type-Specific Auto-Completion
It also prints the last command containing the input string. It can be inserted by pressing the right arrow key ().

PowerShell auto-completion with type-based suggestions

Yes, what you see is indeed PowerShell on a Linux distribution.




Object-oriented Approach

Processing complex data is not a problem anymore when you can work with types and utilize one of the most widely used programming paradigms. One can use tons of predefined types from the .NET platform or create their own.

Everything is heavily documented:

Here's a quick example. We have created a custom class User that has a Name, an Age, and a list of Interests. Now we can create objects of this type.

PS /home/deathroll> class User {                                     
>>     [String] $Name                                  
>>     [Int] $Age                                                  
>>     [System.Collections.Generic.List[String]] $Interests
>> 
>>     User($Name, $Age, $Interests) { $this.Name = $Name; $this.Age = $Age; $this.Interests = $Interests }
>> }
PS /home/deathroll> [User[]] $ExampleUsers = @(                      
>>     [User]::new("Jonh Doe", 47, @("Who knows O_o")),
>>     [User]::new("Jake Smith", 22, @("Programming", "Unix-like"))
>> )
PS /home/deathroll> $($ExampleUsers[1]).Interests.Add("Writing Docs")
PS /home/deathroll> $ExampleUsers                                    

Name       Age Interests
----       --- ---------
Jonh Doe    47 {Who knows O_o}
Jake Smith  22 {Programming, Unix-like, Writing Docs}

PS /home/deathroll> $ExampleUsers[0].GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    User                                     System.Object

PS /home/deathroll>
Enter fullscreen mode Exit fullscreen mode

Do you feel how empowering is this?

Standard Cmdlet Names

Cmdlets is the PowerShell's way of referring to what we know as "commands."

There's a document for PowerShell that defines how cmdlets should be named. According to it, each cmdlet consists of a verb and a noun. This makes the names easy to read, understand, and memorize. All of the predefined commands adhere to such a convention.

Is it obvious what this command does?

PS /home/deathroll> ConvertTo-Json -InputObject $ExampleUsers

Output
[
  {
    "Name": "Jonh Doe",
    "Age": 47,
    "Interests": [
      "Who knows O_o"
    ]
  },
  {
    "Name": "Jake Smith",
    "Age": 22,
    "Interests": [
      "Programming",
      "Unix-like",
      "Writing Docs"
    ]
  }
]

This is just a recommendation, and people don't have to follow it, but it's better to do so to be consistent.

Approved verbs

Script and Function Parameters

Please note that the script below may not be secure or heavily polished. Also, it's not a good idea to generate passwords in such a way, but that was part of an assignment to simplify things a bit.

Remember I mentioned a script in the Preamble of this article?

Here it is:

Have you already noticed the Param() statement? That's how you define parameters for a script or a function. No more $1, $2 and case...esac, my friend.

Just look at this beauty:

PS /home/deathroll> function Greet-User {
>>     param([Switch] $Informal, [String] $UserName)
>> 
>>     Write-Host $($(If ($Informal) { "Hey" } Else { "Hello" } ) + ", $UserName")
>> } 
PS /home/deathroll> Greet-User $env:USER      
Hello, deathroll
PS /home/deathroll> Greet-User $env:USER -Informal
Hey, deathroll
PS /home/deathroll>
Enter fullscreen mode Exit fullscreen mode

There are multiple types of parameters that can be declared:

  • dynamic (available only in certain circumstances)
  • static
  • switch (work like on and off flags)

They can have attributes like Mandatory (required) and so on.

And a cherry on top of this is that PowerShell reads parameters defined in a script and can perform their names auto-completion when you type them on the command line.

This is an example for PowerShell >=7.0. It contains the ternary operator.

PS /home/deathroll> Get-Content ./Greet.ps1 # Read the contents of the file, like the `cat` command.           
Param([Switch] $Informal, [String] $UserName)

Write-Host $($($Informal ? "Hey" : "Hello") + ", $UserName")
PS /home/deathroll> ./Greet.ps1 - # Here the TAB key was smashed :D
Informal  UserName  
PS /home/deathroll> ./Greet.ps1 -Informal $env:USER
Hey, deathroll
PS /home/deathroll>
Enter fullscreen mode Exit fullscreen mode

Full Integration With the Underlying Platform (.NET)

While everything aforementioned are killer features, this is a killer-killer feature.

Normally we complement shell scripts with Python, Ruby, JavaScript, or whatever scripting languages if the logic needs to be more complex. But in theory, we could just use PowerShell for all of this and tackle specific tasks by calling the .NET API where needed. Maybe even extend the shell itself using other .NET languages like C#.

Conclusion

PowerShell borrows some features from Unix shells: reverse and forward history search, pipes, etc., and adds tons of useful stuff. Why don't we borrow features from it or accept the technology itself where it can make our lives easier? Think about it.

Meanwhile, I'll discuss with our team if we should use PowerShell to automate things on Linux.

Top comments (0)