Search
Joe Pruitt - A Software Architect's take on Network Security
You are here: DevCentral > Weblogs

posted on Monday, May 04, 2009 9:30 AM

PowerShell_unix PowerShell is definitely gaining momentum in the windows scripting world but I still hear folks wanting to rely on Unix based tools to get their job done.  In this series of posts I’m going to look at converting some of the more popular Unix based tools to PowerShell.

basename

The Unix “basename” command will delete any prefix up to the last slash (‘/’) character and return the result. 

For my PowerShell implementation, I’ve switched the slash path separator character with the Windows backslash (‘\’) path separator. 

PowerShell does have the Split-Path cmdlet that will parse a path into it’s components.  And with it’s “-Leaf” argument, you can extract the basename equivalent.  My first implementation used this route, but the Split-Path cmdlet didn’t work correctly with relative paths.  More specifically, the Unix command passing in “.” would return “.”.  PowerShell returns the value of the current directory.   I tested out the “-LiteralPath” option but it still required a “-Path” option and would not interpret the “.” or “..” as literal strings.

So, I went ahead and wrote a little reverse string walking loop which turns out to be about 25% faster than using “Split-Path –Leaf”.  I’ve also included in this script a few unit tests I ran to verify the output.

 

   1: #----------------------------------------------------------------
   2: # Dirname.ps1
   3: #----------------------------------------------------------------
   4: param
   5: (
   6:   [string]$name = $null
   7: );
   8:  
   9: $script:SEPARATOR = "\";
  10:  
  11: #----------------------------------------------------------------
  12: # function Do-Basename
  13: #----------------------------------------------------------------
  14: function Do-Basename()
  15: {
  16:   param
  17:   (
  18:     [string]$name = $null
  19:   );
  20:   
  21:   $basename = "";
  22:   
  23:   if ( $name  )
  24:   {
  25:     if ( $name.EndsWith($script:SEPARATOR) )
  26:     {
  27:       $name = $name.SubString(0, $name.Length -1);
  28:     }
  29:     $i = 0;
  30:     for ($i = $name.Length-1; $i -ge 0; $i--)
  31:     {
  32:       if ( $name[$i] -eq $script:SEPARATOR )
  33:       {
  34:         break;
  35:       }
  36:     }
  37:     if ( $i -ge 0 )
  38:     {
  39:       $basename = $name.SubString($i+1)
  40:     }
  41:     else
  42:     {
  43:       $basename = $name;
  44:     }
  45:   }
  46:   $basename;
  47: }
  48:  
  49: #----------------------------------------------------------------
  50: # function Do-BasenameUnitTests
  51: #----------------------------------------------------------------
  52: function Do-BasenameUnitTests()
  53: {
  54:   $tests = @( 
  55:     @("foo", "foo"),
  56:     @("foo\", "foo"),
  57:     @("", ""),
  58:     @("\foo", "foo"),
  59:     @("\foo\", "foo"),
  60:     @("\foo\bar", "bar"),
  61:     @("\foo\bar\", "bar"),
  62:     @("c:\foo", "foo"),
  63:     @("c:\foo\bar", "bar"),
  64:     @("c:\foo\bar\", "bar"),
  65:     @("c:\foo\bar\ ", " "),
  66:     @(".", "."),
  67:     @("..", ".."),
  68:     @(".\", "."),
  69:     @("..\", ".."),
  70:     @("..\.", "."),
  71:     @("..\.\foo", "foo")
  72:   );
  73:   
  74:   $success = "PASS";
  75:   
  76:   "  {0,-15}      {1,-15}       {2,-15}    {3}" -f ("Test", "Expected", "Found", "Pass");
  77:   foreach ($test in $tests)
  78:   {
  79:     $result = Do-Basename $test[0];
  80:     $status = $result -eq $test[1];
  81:     "({0,-15} -> {1,-15}) -> {2,-15} : {3}" -f ("""$($test[0])""", """$($test[1])""", """$($result)""", $status);
  82:     #Write-Host "TEST: (""$($test[0])"" -> ""$($test[1])"") -> ""$result"" : $status";
  83:     if ( ! $status ) { $success = "FAIL"; }
  84:   }
  85:   ""
  86:   "RESULT: $success";
  87: }
  88:  
  89: Do-Basename -name $name;
  90: #Do-BasenameUnitTests;

You can download the full script here: Basename.ps1



Feedback

5/4/2009 9:55 AM
Gravatar FYI, PowerShell V2 includes a Basename NoteProperty on every FileInfo object e.g.:

11> ls | select basename

BaseName
--------
Windows Virtual PC Beta - X64
AdbeRdr813_en_US
TweetDeck_0_25

For PowerShell V1, PowerShell Community Extensions provides this same NoteProperty. Also rather than use substring manipulation, just use [System.IO.Path]::GetFileNameWithoutExtension($path).
Keith Hill
5/4/2009 10:01 AM
Gravatar The problem with the internal commands is that they don't treat the paths as strings. They try to convert them to local paths first and the "-LiteralPath" option doesn't seem to work in V2. For paths like ".", the Unix command will return "." while the PowerShell/.Net options return the current directory. I guess I could have went with the GetFileNameWithoutExtension route but I would have had to have a few special cases as well to get the output to be the same as the Unix command. But that one line would have replaced only that couple line for loop. I'll have to do some timing tests with that route though to see if the pure script route is faster like it is with the Split-Path cmdlet.

Thanks for the feedback, I learn something new each day about PowerShell through these posts.

-Joe
Joe Pruitt
12/11/2010 4:26 AM
Gravatar nätcasinon
I want to move the sidebar to top in wizard control. How to do that??? I know control adapters can help but it is too tough to write those. Let me know how to do that. Any other solution is also welcome.
natasha
5/14/2011 3:17 AM
Gravatar Feeling gratitude and not expressing it is like wrapping a present and not giving it. So it would be marked with the lot of enthusiasm and appreciation
Computers and Internet
7/28/2011 10:17 AM
Gravatar Hey Joe, your solution seems to be the complete implementation of the UNIX equivalent. For my purposes, a much more limited version, which worked well for my cases was this:

Param([parameter(Mandatory=$true)] [string] $PathName)
$Pathname -replace '^(.+)\\([^\\]+)$', '$2'

I am sure it will not pass all your tests, but possibly with a couple of additional regexes and a few tests on them you could cover all the cases.
Coffee-fan

Let Me Know What You Think


Please use the form below if you have any comments, questions, or suggestions.

Title:
 
Name:
 
Email: (so we can show your gravatar)
Website:
Comment: Allowed tags: blockquote, a, strong, em, p, u, strike, super, sub, code
 
Please add 1 and 2 and type the answer here:

Blog Stats

Posts:379
Comments:1067
Stories:1
Trackbacks:301
  

Article Categories

  iRules
  

Image Galleries

  

Joe's bookshelf: read

The Lost Gate
4 of 5 stars
This one started slow but I got really got into it about 1/3 of the way through. If you are an Ender's Game fan, you'll probably like this one as well.

goodreads.com


82,243 Members in 102 Countries and Growing!

Join DevCentral Today!

About DevCentral

DevCentral has been a successful, thriving community for many years. We have always strived to bring you the best technical documentation, discussion forums, blogs, media and much more that we can.

So dive in, get familiar with DevCentral. We hope you like it, we hope it makes your job easier, and lets you get that much more power out of the community. To learn more, make sure to check out the Getting Started section. And if you have any problems, or think something could be easier to use, drop us a line to let us know.

Got It !

We've received your comment and transmitted it directly to DevCentral HQ.

Thanks for taking time to let us know what's on your mind. At DevCentral | Community Matters!

Get In Touch With Us

Have questions, suggestions or just want to get something off your chest?

Use our handy form below to Direct Connect with DevCentral Mission Control.

Send Us Feedback       or