Comparative Examples in MSH and KSH

Comparative Examples in MSH and KSH

Rate This
  • Comments 53

 

Most shells (such as Windows CMD.EXE and the UNIX shells SH, KSH, CSH, and BASH) operate by executing a command or utility in a new process, and presenting the results (or errors) to the user as text. Text-based processing is the way in which system interaction is done with these shells. Over the years, a large number of text processing utilities—such as sed, AWK, and PERL—have evolved to support this interaction. The heritage of this operational process is very rich.

MSH is very different from these traditional shells. First, this shell does not use text as the basis for interaction with the system, but uses an object model based on the .NET platform. Second, the list of built-in commands is much larger; this is done to ensure that the interaction with the object model is accomplished with the highest regard to integrity with respect to interacting with the system. Third, the shell provides consistency with regard to interacting with built-in commands through the use of a single parser, rather than relying on each command to create its own parser for parameters.

This document constrasts the Korn Shell (KSH) and MSH by providing a number of examples of each.

Example 1

To stop all processes that begin with the letter "p" on a UNIX system, an administrator would have to type the following shell command line:

$ ps -e | grep " p" | awk '{ print $1 }' | xargs kill

The ps command retrieves list of processes and directs (|) the grep command to determine which process begins with "p"; which in turn directs (|) the awk command to select the 1st column (which is in the process id) and then passes (|) those to the xargs command which then executes the kill command for each process.  The fragility of this command line may not be evident, but if the ps command behaves differently on different systems, where the "-e" flag may not be present, or if the processed command is not in column 1 of the output, this command-line procedure will fail.

MSH:

MSH> get-process p* | stop-process

Thus, the command line may now be expressed as "get the processes whose name starts with "p" and stop them".  The get-process Cmdlet takes an argument that matches the process name; the objects returned by get-process are passed directly to the stop-process Cmdlet that acts on those objects by stopping them.

The second, more convoluted example, which stops processes that use more than 10 MB of memory becomes quite simple.

Example 2

Another, even more complex example, such as "find the processes that use more than 10 MB of memory and kill them" can lead to an equally failed outcome:

$ ps -el | awk '{ if ( $6 > (1024*10)) { print $3 } }' | grep -v PID | xargs kill

The success of this command line relies on the user knowing that the ps -el command will return the size of the process in kilobytes (kb) in column 6 and that the PID of the process is in column 3.  It is still required that the first row is removed.

Comparing Example 1 using a standard shell to Example 1a using MSH, we can see that the commands act against objects rather than against text.

MSH:

MSH> get-process | where { $_.WS -gt 10MB } | stop-process

There is no issue about determining the column that contains the size of the process, or which column contains the ProcessID.  The memory size may be referred to logically, by its name.  The where Cmdlet can inspect the incoming object directly and refer to its properties.  The comparison of the value for that property is direct and understandable.

Example 3

For example, if you wanted to calculate the number of bytes in the files in a directory, you would iterate over the files, getting the length and adding to a variable, and then print the variable:

$ tot=0; for file in $( ls )

> do

>     set -- $( ls -log $file  )

>     echo $3

>     (( tot = $tot + $3 ))

> done; echo $tot

This example uses the set shell command that creates numbered variables for each white space separated element in the line rather than the awk command as in the examples above.  If the awk command were used, it would be possible to reduce the steps to the following:

$ ls –l | awk ‘{ tot += $5; print tot; }’ | tail -1

This reduces the complexity, but requires specific knowledge of a new language, the language that is associated with the awk command.

The MSH loop is similar; each file in the directory is needed, but it is far simpler as the information about the file is already retrieved:

MSH:

MSH> get-childitem | measure-object -Property length -Sum

The measure-object Cmdlet interacts with objects and if it is provided with a property from the object, it will sum the values of that property.  Because the property length represents the length of the file, the measure-object Cmdlet is able to act directly on the object by referring to the property name rather than “knowing” that the length of the file is in column 3 or column 5.

Example 4

Many objects provided by the system are not static, but dynamic.  This means that after an object is acquired, it is not necessary to acquire the object at a later time.  The data in the object is updated as the conditions of the system change.  Also, changes to these objects are reflected immediately in the system.

As an example, suppose one wanted to collect the amount of processor time that a process used over time.  In the traditional UNIX model, the ps command would need to be run iteratively and the appropriate column in the output would need to be found and then the subtraction would need to be done.  With a shell that is able to access the process object of the system, it is possible to acquire the process object once, and since this object is continually updated by the system; all that is necessary is to refer to the property.  The following examples illustrate the differences, where the memory size of an application is checked in ten second intervals and the differences are output:

$ while [ true ]

do

   msize1=$( ps -el | grep application | grep -v grep | awk '{  print $6 }' )

   sleep 10

   msize2=$( ps -el | grep application | grep -v grep | awk '{  print $6 }' )

   expr $msize2 - $msize1

   msize1=$msize2

done

MSH:

MSH> $app = get-process application

MSH> while ( 1 ) {      

>> $msize1 = $app.VS

>> start-sleep 10

>> $app.VS - $msize1  

>> }

Example 5

It is even more difficult to determine whether a specific process is no longer running.  In this case, the UNIX user must collect the list of processes and compare them to another list. 

$ processToWatch=$( ps -e | grep application | awk '{ print $1 }'

$ while [ true ]

> do

>     sleep 10

>     processToCheck=$(ps -e | grep application | awk '{ print $1 }' )

>     if [ -z "$processToCheck" -or "$processToWatch" != "$processToCheck" ]

>     then

>        echo "Process application is not running"

>        return

>     fi

> done

MSH:

MSH> $processToWatch = get-process application

MSH> $processToWatch.WaitForExit()

As is seen in this example, the MSH user need only collect the object and then subsequently refer to that object. 

Example 6

For example, suppose the user wanted to determine which processes were compiled as PreRelease code, such as when applications have been compiled in such a way to mark them as "PreRelease".

$ ???

This information is not kept in the standard UNIX executable.  To determine this information, one would need to have a set of specialized utilities to add this information to the binary and then another set of utilities to collect this information.  These utilities do not exist; it is not possible to accomplish this task.

MSH:

MSH> get-process | where {$_.mainmodule.FileVersioninfo.isPreRelease}

Handles  NPM(K)    PM(K)      WS(K) VS(M)   CPU(s)     Id ProcessName

-------  ------    -----      ----- -----   ------     -- -----------

    643      88     1024       1544    15    14.06   1700 AdtAgent

    453      15    25280       7268   199    91.70   3952 devenv

In this example, a cascade of properties is done.  The appropriate property from the process object (MainModule) is inspected, the property "FileVersionInfo" is referenced (a property of MainModule) and the value of the property "IsPreRelease" is used to filter the results.  If IsPreRelease is true, the objects that are output by the get-process Cmdlet are output.

Example 7

Each object may or may not provide methods; MSH provides commands to aid the discovery of methods that are available for a specific object via the get-member Cmdlet.  For example, the string object has a large number of methods:

MSH:

MSH> get-member -input "String" -membertype method | format-table "$_"

"$_"

---------

System.Object Clone()

Int32 Compare(System.String, System.String, Boolean, System.Globalization.Cul..

Int32 CompareOrdinal(System.String, Int32, System.String, Int32, Int32)

Int32 CompareTo(System.String)

System.String Concat(System.String, System.String)

Boolean Contains(System.String)

System.String Copy(System.String)

Void CopyTo(Int32, Char[], Int32, Int32)

Boolean EndsWith(System.String, Boolean, System.Globalization.CultureInfo)

Boolean Equals(System.String, System.StringComparison)

System.String Format(System.String, System.Object, System.Object)

Char get_Chars(Int32)

Int32 get_Length()

System.CharEnumerator GetEnumerator()

Int32 GetHashCode()

System.Type GetType()

System.TypeCode GetTypeCode()

Int32 IndexOf(System.String, Int32)

Int32 IndexOfAny(Char[], Int32, Int32)

System.String Insert(Int32, System.String)

System.String Intern(System.String)

System.String IsInterned(System.String)

Boolean IsNormalized()

Boolean IsNullOrEmpty(System.String)

System.String Join(System.String, System.String[])

Int32 LastIndexOf(Char, Int32)

Int32 LastIndexOfAny(Char[], Int32)

System.String Normalize(System.Text.NormalizationForm)

Boolean op_Equality(System.String, System.String)

Boolean op_Inequality(System.String, System.String)

System.String PadLeft(Int32, Char)

System.String PadRight(Int32, Char)

System.String Remove(Int32)

System.String Replace(System.String, System.String)

System.String[] Split(System.String[], System.StringSplitOptions)

Boolean StartsWith(System.String)

System.String Substring(Int32)

Char[] ToCharArray(Int32, Int32)

System.String ToLower()

System.String ToLowerInvariant()

System.String ToString(System.IFormatProvider)

System.String ToUpper(System.Globalization.CultureInfo)

System.String ToUpperInvariant()

System.String Trim()

System.String TrimEnd(Char[])

System.String TrimStart(Char[])

As can be seen, 46 different methods are available to the string object all of which are available to the MSH user.  Unfortunately, the semantics of these methods is not visible from the shell, but a number of .NET object help is available online.

Example 8

The availability of these methods creates an explosion of possibilities.  For example, if I wanted to change the case of a string from lower to upper I would do the following: (first ksh and then MSH).

$ echo "this is a string" | tr [:lower:] [:upper:]

or

$ echo "this is a string" | tr '[a-z]' '[A-Z]'

MSH:

MSH> "this is a string".ToUpper()

The UNIX example relies on the tr cmdlet. 

Example 9

For example, suppose the string "ABC" was to be inserted after the first character in the word "string" to have the result "sABCtring".  Here are the following, first with ksh, then with MSH:

$ echo "string" | sed "s|\(.\)\(.*)|\1ABC\2|"

MSH:

MSH> "string".Insert(1,"ABC")

Both examples require specific knowledge; however, using the "insert" method is more intuitive than using the capture buffers available in sed.  Moreover, the domain specific knowledge of the "sed" language required may be somewhat more advanced than is required to use the Insert method.





Jim Truher and Jeffrey Snover

[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]

PSMDTAG:FAQ: KSH BASH - how does PowerShell compare to KSH and BASH?
PSMDTAG:DOTNET: Leverage System.String to do KSH/BASH type functions.

Leave a Comment
  • Please add 5 and 1 and type the answer here:
  • Post
  • PingBack from http://blog.3deurope.com/index.php/2006/05/01/cool-post-on-msh/
  • PingBack from http://www.quiddit.ch/?p=66
  • It looks like the PowerShell is an Object Oriented view of System. But still: is there really something new? I thing all that we already saw somewhere around (AmigaOS, unix/linux, MacOS). We have to wait for applications to start support it (implement methods), then it start to be very interesting.

  • Isn't the first argument (that 'ps' may behave differently on different systems) silly? Windows is one system, so of course you don't have subtle interoperability problems. 'ps' doesn't work the same for Windows as it does for Unix either; it doesn't work at all! On the other hand, if you're POSIX, this stuff has to work the same way for the same switches (in addition to the vendor's superset of commands for ps, ls, etc)

    Example 3 isn't so good either. One would simply use 'du' in that situation.

    Example 4 really do the same thing the same way; the only difference is notation. Give me the time now and find the difference. Either way, you had to figure out how to get that metric and fetch it twice.

    Example 6 has nothing to do with any Unix shell.

    I'm not saying that PowerShell isn't a step forward (I don't know yet, I plan on playing with it at work...but anything beats cmd or wsh), but these arguments are bogus.

  • Hello,

    Easier way to do...

    Example 1 :

    => use pkill

    # pkill -9 vi

    [1]   Killed                  vim

    [2]   Killed                  vim

    Example 3 :

    user du

    # du -sh /opt

     18M   /opt

  • Example 3

    For example, if you wanted to calculate the number of bytes in the files in a directory, you would do that with MSH:

    MSH> get-childitem | measure-object -Property length

    Where you just need to call the "du" command in a standard shell:

    SH> du

  • Is their a method to store the result of a get-childitem (or any other command) who should be reusable in the same way as the original result ?

    With a standard shell this two commands have same result:

    ls|mycommand

    and

    ls > file

    cat file|mycommand

    So you can store the result of a command and resuse it later in the same way. Is it possible with PowerShell ?

    In this case, is the stored data human readable ?

    Do you plan to provide a better terminal emulator than cmd.exe ?

    Should MSH be usable in any other terminal emulator like bash, ksh and others do ?

    I'd like to add an alias on one of my network interfaces with a new IP address, to set a firewall rule that allow access to port 80 on this address, and then to add a VirtualHost on my IIS server with this IP address. How to with MSH ?

  • "cmdlet" ? Since when is this suffix back in style ? Don't you have people in your company whose job is to prevent developpers to use lame suffix ?

    Now, excuse-me, but I'm going to take my breakfastlet and then have a showerlet before I go to worklet have my joblet done.

  • Very good job by MS. The power of .NET in the fingers of the Windows Administrators.

  • This article want to treat UNIX as a bad thing willfully. There're much simplier solutions like pkill and du commands instead of using awk and OS dependent parameteres of ps, and such. Also please note that even the whole terminology used by Windows nowdays was created by UNIX, eg the filesystem with directories (the notion of directory, or folder or whatever you want they to be called), device nodes as files, pipes (|), etc. Other stuffs are ripped from CP/M, like device letters (C: ...) FCBs and such. So this powershell stuff is good, but don't treat it as a brand new feature which is the default think in UNIX for dozens of years, also do not try to mystify UNIX CLI that it's overcomplicated when there're simple solutions though they're not in this article ... (even simplier than powershell can provide ...).

  • Please send the PowerShell team to a KSH training before creating such examples!

  • Oh, almost forget: please do not allude to the statement that UNIX has many incomaptible implementations: Windows is much more incomaptible for many standards we have, so it's quite false viewpoint :) Also, you can use much more common and compatible and even simplier solutions like eg pkill than using awk/ps/etc mixed together which - no doubt - requires more knowledge to use of course. Please do not misunderstand me: I don't say powershell and/or Windows are evil. I only say that nobody should say false statements to try to make people think that Windows and its solutions are superior without introduce the GOOD solutions for these problems in UNIX, too.

  • Well, considering how MS bashed Unix into the ground, now they are 'reinventing' the Shell...Oooooo...I bet all the Windows admins will be like, "Wow, MS is awesome, look at this sweet command line stuff we can do now!  I bet those UNIX guys wish they could do this."

    History repeats itself...MS just can't get away from UNIX...UNIX is still around today (including Linux) for a good reason!

    UNIX/Linux = Nanometer Legos

    Windows = Duplos

    First they ignore you.

    Then they laugh at you.

    Then they fight you.

    Then you win.

    Ah yes, words of wisdom!

  • > First they ignore you.

    > Then they laugh at you.

    > Then they fight you.

    > Then you win.

    I can't tell whether you are at the "laughing at us" or the "fighting us" stage.  :-)

    Jeffrey Snover [MSFT]

    Windows PowerShell/MMC Architect

    Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell

    Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

  • Lets be honest, Powershell uses an excellent new paradigm.  It borrows much from unix etc but adds much also.  

    However, when doing comparative analysis let's make sure the examples given are reasonable.  

    Powershell has much to recommend it, and can hold it's own is a fair comparison.  It's case is hindered by not doing a fair comparison.  

Page 1 of 4 (53 items) 1234