Welcome!

A Software Architect Living in a Networking World

Joe Pruitt

Subscribe to Joe Pruitt: eMailAlertsEmail Alerts
Get Joe Pruitt via: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


Blog Feed Post

Unix To PowerShell - Dirname

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.

dirname

The Unix “dirname” command will strip any non-directory suffix from a file name.  Given a NAME, dirname will print the name with it’s trailing “/” component removed.  If NAME contains no “/” characters, it will output “.” (meaning the current directory).

Since PowerShell is run on windows, my script will substitute the forward slash with the windows backslash directory specifier.  This can be overridden by changing the $script:SEPARATOR variable in the script.

At first I thought that a simple [System.IO.FileInfo] cast would do the trick, but it doesn’t behave the same if you give a non-fully qualified directory (ie foo.txt, .\foo.txt, ..\foo.txt).  For non-qualified paths, the FileInfo class will default to the $env:home directory (ie. C:\Users\Joe) which is not the behavior of the Unix dirname command.  I also looked at using the builtin Get-ChildItem cmdlet but that will not work for paths that are not present on the file system and this command should work for any string passed in.

I’ve included a few unit test cases with the Do-DirNameUnitTests function to show the inputs and expected outputs.

The script takes only one argument, the name to process and returns the dirname to the output.

   1: #----------------------------------------------------------------
   2: # Dirname.ps1
   3: #----------------------------------------------------------------
   4: param
   5: (
   6:   [string]$name = $null
   7: );
   8:  
   9: $script:SEPARATOR = "\";
  10:  
  11: #----------------------------------------------------------------
  12: # function Do-Dirname
  13: #----------------------------------------------------------------
  14: function Do-Dirname()
  15: {
  16:   param
  17:   (
  18:     [string]$name = $null
  19:   );
  20:   
  21:   $dirname = ".";
  22:   
  23:   if ( $name  )
  24:   {
  25:     if ( $name -ne $script:SEPARATOR )
  26:     {
  27:       $start_index = $name.Length-1;
  28:       
  29:       # for paths like \foo\bar\, bypass the trailing separator
  30:       if ( ($name.Length -gt 1) -and $name.EndsWith($script:SEPARATOR) )
  31:       {
  32:         $start_index--;
  33:       }
  34:       
  35:       # Reverse Iterate through the string until we find a path separator
  36:       for($dirlen=$start_index; $dirlen -ge 0; $dirlen--)
  37:       {
  38:         if ( $name[$dirlen] -eq $script:SEPARATOR )
  39:         {
  40:           break;
  41:         }
  42:       }
  43:       if ( $dirlen -eq -1 )
  44:       {
  45:         # No path separators found, default to the current directory
  46:         $dirname = "."
  47:       }
  48:       else
  49:       {
  50:         $dirname = $name.Substring(0, $dirlen);
  51:         
  52:         #sanity checks
  53:         if ( $dirname.Length -eq 0 ) { $dirname = $script:SEPARATOR; }
  54:         if ( $dirname -like "[a-zA-Z]:" ) { $dirname += $script:SEPARATOR; }
  55:       }
  56:     }
  57:   }
  58:   $dirname;
  59: }
  60:  
  61: #----------------------------------------------------------------
  62: # function Do-DirnameUnitTests
  63: #----------------------------------------------------------------
  64: function Do-DirnameUnitTests()
  65: {
  66:   $tests = @( 
  67:     @("foo", "."),
  68:     @("foo\", "."),
  69:     @("", "."),
  70:     @("\foo", "\"),
  71:     @("\foo\", "\"),
  72:     @("\foo\bar", "\foo"),
  73:     @("\foo\bar\", "\foo"),
  74:     @("c:\foo", "c:\"),
  75:     @("c:\foo\bar", "c:\foo"),
  76:     @("c:\foo\bar\", "c:\foo")
  77:   );
  78:   
  79:   $success = "PASS";
  80:   
  81:   "  {0,-15}      {1,-15}       {2,-15}    {3}" -f ("Test", "Expected", "Found", "Pass");
  82:   foreach ($test in $tests)
  83:   {
  84:     $result = Do-Dirname $test[0];
  85:     $status = $result -eq $test[1];
  86:     "(""{0,-15}"" -> ""{1,-15}"") -> ""{2,-15}"" : {3}" -f ($test[0], $test[1], $result, $status);
  87:     #Write-Host "TEST: (""$($test[0])"" -> ""$($test[1])"") -> ""$result"" : $status";
  88:     if ( ! $status ) { $success = "FAIL"; }
  89:   }
  90:   ""
  91:   "RESULT: $success";
  92:  
  93: }
  94:  
  95: Do-Dirname -name $name;

You can get the full script here: Dirname.ps1

Read the original blog entry...

More Stories By Joe Pruitt

Joe Pruitt is a Principal Strategic Architect at F5 Networks working with Network and Software Architects to allow them to build network intelligence into their applications.