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>
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>
Yes, what you see is indeed PowerShell on a Linux distribution.Type-Specific Auto-Completion
It also prints the last command containing the input string. It can be inserted by pressing the right arrow key (→
).
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>
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.
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>
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>
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)