Saturday, June 20, 2009

Unix Equivalents in Powershell

I come from Linux background which I really like . There are lot more utilities in Unix/Linux which provide more convenient way of doing some work. They are really very useful in speeding up the work and automating repetitive tasks.

Since the release of Windows Powershell, it has proven to be one of the most powerful shells available now.

As I started using Powershell, I find myself most of the times searching for UNIX equivalents in windows powershell. Here are a few which i came across

wc in Powershell: Measure-Object


[jagadish.g@localhost Scripts]$ cat test.csv | wc
4 13 277
[jagadish.g@localhost Scripts]$ cat test.csv | wc -l
4


PS C:\Scripts> Get-Content test.csv | Measure-Object -line -word -character

Lines Words Characters Property
----- ----- ---------- --------
4 13 273

PS C:\Scripts> Get-Content test.csv | Measure-Object -line

Lines Words Characters Property
----- ----- ---------- --------
4

time in Powershell: Measure-Command


[jagadish.g@localhost Scripts]$ time sleep 5

real 0m5.003s
user 0m0.000s
sys 0m0.001s


PS C:\Scripts> Measure-Command { Sleep 5 }

Days : 0
Hours : 0
Minutes : 0
Seconds : 4
Milliseconds : 999
Ticks : 49996681
TotalDays : 5.78665289351852E-05
TotalHours : 0.00138879669444444
TotalMinutes : 0.0833278016666667
TotalSeconds : 4.9996681
TotalMilliseconds : 4999.6681

GREP in Powershell: Select-String


[jagadish.g@localhost Scripts]$ cat test.csv | grep "Tony Passaquale"
1,Tony Passaquale,7920,20090222 21:59:00,800,4.78,3824,Follow-up


PS C:\Scripts> Get-Content test.csv | Select-String "Tony Passaquale"

1,Tony Passaquale,7920,20090222 21:59:00,800,4.78,3824,Follow-up

NOTE, Select-String is Case-Insensitive by default. Use -CaseSensitive switch parameter to make it case sensitive.

Now lets try to get one line after the line with the match


[jagadish.g@localhost Scripts]$ cat test.csv | grep -A1 "Tony Passaquale"
1,Tony Passaquale,7920,20090222 21:59:00,800,4.78,3824,Follow-up
2,Nigel Shan Shanford,30316,20090405 16:34:00,400,9.99,3996,New-Opportunity


PS C:\Scripts> Get-Content test.csv | Select-String "Tony Passaquale" -Context 0,1

> 1,Tony Passaquale,7920,20090222 21:59:00,800,4.78,3824,Follow-up
2,Nigel Shan Shanford,30316,20090405 16:34:00,400,9.99,3996,New-Opportunity

The -Context parameter in the Select-String cmdlet is used to capture the specified number of lines before and after the line with the match. If you look at the above code window, I have passed '0,1' value to the -Context parameter. The first integer specifies the number of lines to print before the match (in this case, it is 0) and the next integer specifies the number of lines to print after the match (in this case, it is 1)

Select-String cmdlet is using regular expression matching by default. So you can use use regex patterns to select the required lines.


[jagadish.g@localhost Scripts]$ cat test.csv | grep "y$"
2,Nigel Shan Shanford,30316,20090405 16:34:00,400,9.99,3996,New-Opportunity
4,Allen James,95140,20090405 16:31:00,1000,9.99,9990,New-Opportunity


PS C:\Scripts> Get-Content test.csv | Select-String "y$"

2,Nigel Shan Shanford,30316,20090405 16:34:00,400,9.99,3996,New-Opportunity
4,Allen James,95140,20090405 16:31:00,1000,9.99,9990,New-Opportunity

AWK in Powershell

Powershell has lot of features and abilities for text parsing. AWK is one of very powerful commands available for text parsing in Unix/Linux. We do not have a Awk like cmdlet in Powershell. But we can do everything in Powershell that can be done with Awk.

Powershell combined with .Net Classes provide very powerful regular expressions for text parsing. read more...

cd - in Powershell

One of the cool features of linux bash is the ability to go to the previous working directory. In Bash, we can go to the previous working directory using "cd -" command.

Unfortunately, we do not have an equivalent of "cd -" in Powershell. So I just wrote a simple powershell function for this cool feature. read more...

find command in Powershell

Find is one of the most often used commands in day to day work life. Unix find command provides lot of features and options that enable users to find files/directories more effectively

In Powershell, we can use Get-ChildItem cmdlet to search for files. But it doesn't give you all the fun. So I have developed Find-ChildItem Powershell cmdlet which is equivalent to Unix find command. read more...

Saturday, June 13, 2009

Regular Expressions in Powershell

Regular Expressions are the most powerful pattern matching techniques available for text parsing. Almost all programming languages have regular expressions. Perl has very powerful regex engine and it provides more features.

Windows Powershell carries excellent regular expression support from .Net Framework

Regex Pattern matching in Powershell is so easy. We will go though some examples on Regular Expressions in Windows Powershell

Prerequisite: Some knowledge on regular expression would be good


PS C:\Scripts> 'Hello World' -match '^H'
True
PS C:\Scripts> 'ello World' -match '^H'
False

Powershell returns True if the match is found, otherwise False is returned

Let's see one more example


PS C:\Scripts> '2009-Jun-13' -match '\d{4}-[A-z]{3}-\d{2}'
True
PS C:\Scripts> '2009-June-13' -match '\d{4}-[A-z]{3}-\d{2}'
False

-replace operator is used for Search and Replace in Powershell


PS C:\Scripts> '2009-June-13' -replace "2009", "2010"
2010-June-13

How do you capture the pattern and use it in Search and Replace in Powershell? Lets see how,


PS C:\Scripts> '2009-Jun-13' -replace "(\d{4})-([A-z]{3})-(\d{2})", "$2 $3 $1"

PS C:\Scripts> '2009-Jun-13' -replace "(\d{4})-([A-z]{3})-(\d{2})", '$2 $3 $1'
Jun 13 2009

In Search and Replace, Powershell stores captured patterns in special variables $1, $2, etc. But if you use those special variables inside double-quoted replacements, they will be considered as normal variables. That's the reason, we didn't get expected result in first statement in the above code window. To make it work, you need to use single-quoted replacements like above.

To get more idea on this, let's see another example


PS C:\Scripts> $1="How"
PS C:\Scripts> $2="are"
PS C:\Scripts> $3="you"
PS C:\Scripts>
PS C:\Scripts> '2009-Jun-13' -replace "(\d{4})-([A-z]{3})-(\d{2})", "$2 $3 $1"
are you How
PS C:\Scripts> '2009-Jun-13' -replace "(\d{4})-([A-z]{3})-(\d{2})", '$2 $3 $1'
Jun 13 2009

Now let's see how to use captured patterns with -match operator. If you capture patterns using -match, unlike Search and Replace, the captured patterns will be stored in a special array variable called $matches.


PS C:\Scripts> '2009-Jun-13' -match '(\d{4}).*'
True
PS C:\Scripts> $matches

Name Value
---- -----
1 2009
0 2009-Jun-13

PS C:\Scripts> $matches[1]
2009
PS C:\Scripts>

Regular Expressions in Powershell are case-insensitive by default. For case-sensitive pattern matching, we can use the following operators

1. -cmatch
2. -creplace


PS C:\Scripts> 'HELLO' -match '[A-Z]'
True
PS C:\Scripts> 'HELLO' -match '[a-z]'
True
PS C:\Scripts>
PS C:\Scripts> 'HELLO' -cmatch '[A-Z]'
True
PS C:\Scripts> 'HELLO' -cmatch '[a-z]'
False
PS C:\Scripts>

Saturday, June 6, 2009

AWK equivalent in Windows Powershell

Powershell has lot of features and abilities for text parsing. AWK is one of very powerful commands available for text parsing in Unix/Linux. We do not have a Awk like cmdlet in Powershell. But we can do everything in Powershell that can be done with Awk.

Powershell combined with .Net Classes provide very powerful regular expressions for text parsing.

Now, lets play with Bash Awk and Powershell. I'm going to create a test.csv file for our testing and use that through out this topic.

Content of test.csv for our testing

1,Tony Passaquale,7920,20090222 21:59:00,800,4.78,3824,Follow-up
2,Nigel Shan Shanford,30316,20090405 16:34:00,400,9.99,3996,New-Opportunity
3,Selma Cooper,97455,20090405 16:31:00,1000,9.99,9990,Pre-Approach
4,Allen James,95140,20090405 16:31:00,1000,9.99,9990,New-Opportunity


So, Lets jump in

Display second field in test.csv


[jagadish.g@localhost Scripts]$ cat test.csv | awk -F, '{print $2}'
Tony Passaquale
Nigel Shan Shanford
Selma Cooper
Allen James


PS C:\Scripts> Get-Content .\test.csv | %{ $_.Split(',')[1]; }
Tony Passaquale
Nigel Shan Shanford
Selma Cooper
Allen James

Now, lets try getting the total value of third field in all the records in test.csv


[jagadish.g@localhost Scripts]$ cat test.csv | awk -F, '{total+=$3} END {print "Total: "total}'
Total: 230831


PS C:\Scripts> Get-Content .\test.csv | %{ [int]$total+=$_.Split(',')[2]; } ; Write-Host "Total: $total"
Total: 230831

Get no of fields in each record


[jagadish.g@localhost Scripts]$ cat test.csv | awk -F, '{print "No of fields in record "$1" = "NF }'
No of fields in record 1 = 8
No of fields in record 2 = 8
No of fields in record 3 = 8
No of fields in record 4 = 8


PS C:\Scripts> Get-Content .\test.csv | %{ $a=$_.Split(','); Write-Host "No of fields in record"$a[0]"="$a.length; }
No of fields in record 1 = 8
No of fields in record 2 = 8
No of fields in record 3 = 8
No of fields in record 4 = 8

Regular Expression matching in Awk and Powershell. Print a record if the last field contains any of these lowercase characters (a, b or c)


[jagadish.g@localhost Scripts]$ cat test.csv | awk -F, '{if ($NF ~ "[a-c]") print}'
3,Selma Cooper,97455,20090405 16:31:00,1000,9.99,9990,Pre-Approach


PS C:\Scripts> Get-Content .\test.csv | %{ if ($_.Split(',')[-1] -match "[a-c]") { $_; } }
3,Selma Cooper,97455,20090405 16:31:00,1000,9.99,9990,Pre-Approach

Monday, June 1, 2009

Working with Registry in Powershell

Microsoft has made registry accessing very simple using powershell. You can access the system registry just like any other drive in powershell.

For example, you can easily list all the registry entries in HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run registry key with Get-ItemProperty cmdlet


PS C:\> cd HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
PS HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run> Get-ItemProperty .

RTHDCPL : RTHDCPL.EXE
Alcmtr : ALCMTR.EXE
IgfxTray : C:\WINDOWS\system32\igfxtray.exe
HotKeysCmds : C:\WINDOWS\system32\hkcmd.exe
Persistence : C:\WINDOWS\system32\igfxpers.exe

PS HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run>

You can add a new entry to registry key using New-ItemProperty cmdlet

I have just added a entry to HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run to autostart notepad.exe on the windows boot-up


PS C:\> New-ItemProperty -path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run -name "Notepad" -value "C:\WINDOWS\NOTEPAD.EXE" -type string

Notepad
-------
C:\WINDOWS\NOTEPAD.EXE

PS C:\> Get-ItemProperty -path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

RTHDCPL : RTHDCPL.EXE
Alcmtr : ALCMTR.EXE
IgfxTray : C:\WINDOWS\system32\igfxtray.exe
HotKeysCmds : C:\WINDOWS\system32\hkcmd.exe
Persistence : C:\WINDOWS\system32\igfxpers.exe
Notepad : C:\WINDOWS\NOTEPAD.EXE

As you might have noticed in the above code window, i have used PropertyType as 'String'. Below is the list of PropertyType Values available for Registry Entries

PropertyType ValueMeaning
BinaryBinary data
DWordA number that is a valid UInt32
ExpandStringA string that can contain environment variables that are dynamically expanded
MultiStringA multiline string
StringAny string value
QWord8 bytes of binary data

A registry entry can be removed from a registry key with the help of Remove-ItemProperty cmdlet


PS C:\> Remove-ItemProperty -path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run -name "Notepad"
PS C:\> Get-ItemProperty -path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

RTHDCPL : RTHDCPL.EXE
Alcmtr : ALCMTR.EXE
IgfxTray : C:\WINDOWS\system32\igfxtray.exe
HotKeysCmds : C:\WINDOWS\system32\hkcmd.exe
Persistence : C:\WINDOWS\system32\igfxpers.exe