Subscribe to Windows IT Pro
April 16, 2009 12:00 AM

Emulating the Dir Command in PowerShell

Try this handy Windows PowerShell script that mimics the way dir works in Cmd.exe
Windows IT Pro
InstantDoc ID #101900
Rating: (0)
Downloads
101900.zip

Listing 1: D.ps1
# d.ps1
# Written by Bill Stewart (bill.stewart@frenchmortuary.com)
#
# Lists items like Cmd.exe's Dir command. I wrote this script because the
# get-childitem cmdlet lacks some of Dir's built-in functionality, and I wanted
# to quickly specify attributes and/or a sorting order without the tedium of
# constructing a where-object filter and/or sort-object hashtables.
#
# When passing parameters to the script, I recommend you use the form
# -parameter:argument (particularly with -attributes, -order, and -timefield)
# due to potential argument conflicts. For example, to list files without the
# archive attribute, you should write '-a:-a'. If you just write '-a -a',
# PowerShell's parser interprets this as the -a parameter specified twice. (You
# can also write -a '-a' or -a "-a", but -a:-a is shorter.)

param ($Path,
       $Attributes,
       $Order,
       $TimeField,
       [Switch] $FullName,
       [Switch] $Recurse,
       [Switch] $Bare,
       [Switch] $Q,
       [Switch] $LiteralPath,
       [Switch] $DefaultOutput,
       [Switch] $Help)

 # Begin Callout A
# Outputs a usage message and exits.
function usage {
  $scriptname = $SCRIPT:MYINVOCATION.MyCommand.Name

  "NAME"
  "    $scriptname"
  ""
  "SYNOPSIS"
  "    Lists items in one or more paths."
  ""
  "SYNTAX"
  "    $scriptname [-path:] [-attributes:] [-order:]"
  "    [-timefield:] [-fullname] [-recurse] [-bare] [-q] [-literalpath]"
  "    [-defaultoutput]"
  ""
  "PARAMETERS"
  "    -path:"
  "        The path(s) to the item(s) to list. Without -defaultoutput, the path(s)"
  "        must be in the file system."
  ""
  "    -attributes:"
  "        Displays items matching any one or more of the following attributes:"
  "            A  Files ready for archiving  L  Links (reparse points)"
  "            D  Directories                N  Normal (no other attributes)"
  "            H  Hidden files/directories   R  Read-only files/directories"
  "            I  Not content-indexed        S  System files/directories"
  "        Prefix an attribute character with '-' to exclude it. Use an empty"
  "        string (') to include all attributes."
  ""
  "    -order:"
  "        Displays items in sorted order."
  "            D  Date (oldest first)     N  Name (alphabetic)"
  "            E  Extension (alphabetic)  S  Size (smallest first)"
  "            G  Group directories"
  "        Prefix a sort order character with '-' to reverse the order. Items are"
  "        sorted in the order specified."
  ""
  "    -timefield:"
  "        Controls which time field is displayed and/or used for sorting."
  "            A  Last access time  W  Last write time"
  "            C  Creation time"
  ""
  "    -fullname"
  "        Displays items' full names."
  ""
  "    -recurse"
  "        Recurse through subdirectories. Note: -recurse enables -fullname. When"
  "        using -recurse, -path must contain only directory names."
  ""
  "    -bare"
  "        Displays items' names only."
  ""
  "    -q"
  "        Displays the owner for each item."
  ""
  "    -literalpath"
  "        Specifies that paths are literal (i.e., no characters are interpreted"
  "        as wildcards)."
  ""
  "    -defaultoutput"
  "        Outputs objects instead of formatted strings."

  exit
}
# End Callout A


# Begin Callout B
# If $expr is True, execute $t; otherwise, execute $f.
function iif([ScriptBlock] $expr, [ScriptBlock] $t, [ScriptBlock] $f) {
  if (& $expr) {
    & $t
  } else {
    & $f
  }
}
# End Callout B

# Begin Callout C
# Based on the specified attribute string, this function returns two bitmap
# values. The first bitmap contains the attributes to be included, and the
# second bitmap contains the attributes to be excluded.
function get-attributeflags($attrString) {
  # Create hash table containing the list of file system attributes.
  $attrHash = @{"A" = [System.IO.FileAttributes]::Archive;
                "D" = [System.IO.FileAttributes]::Directory;
                "H" = [System.IO.FileAttributes]::Hidden;
                "I" = [System.IO.FileAttributes]::NotContentIndexed;
                "L" = [System.IO.FileAttributes]::ReparsePoint;
                "N" = [System.IO.FileAttributes]::Normal;
                "R" = [System.IO.FileAttributes]::ReadOnly;
                "S" = [System.IO.FileAttributes]::System}

  $includeFlags = 0    # Attributes to be included
  $excludeFlags = 0    # Attributes to be excluded

  # Create a string containing a list of valid attribute characters.
  $attrChars = ""
  $attrHash.Keys | foreach-object { $attrChars += $_ }

  # Keep track of whether '-' appears before an attribute character.
  $enableFlag = $TRUE

  # Iterate the attribute string as a character array.
  foreach ($attrChar in [Char\[]] $attrString) {
    switch -wildcard ($attrChar) {
      "-" {
        if ($enableFlag) {
          $enableFlag = $FALSE
        }
      }
      "[$attrChars]" {
        $flag = $attrHash\["$_"]
        if ($enableFlag) {
          # Set the bit in the "include" bits.
          $includeFlags = $includeFlags -bor $flag
          # Clear the bit in the "exclude" bits.
          $excludeFlags = $excludeFlags -band (-bnot $flag)
        } else {
          $enableFlag = $TRUE
          # Set the bit in the "exclude" bits.
          $excludeFlags = $excludeFlags -bor $flag
          # Clear the bit in the "include" bits.
          $includeFlags = $includeFlags -band (-bnot $flag)
        }
      }
      default {
        # Throw an error if the attribute character is not valid.
        throw "Invalid attribute character ('$_'). Use -help for help."
      }
    }
  }

  # Output both bit flags.
  $includeFlags,$excludeFlags
}
# End Callout C

#Begin Callout D
# Outputs a list of sort-order hash tables based on the specified sort-order
# string, name field, and time field.
function get-orderlist($orderString, $nameField, $timeField) {
  $orderHash = @{"D" = $timeField;
                 "E" = "Extension";
                 "N" = $nameField;
                 "S" = "Length"}

  # Create string containing a list of valid sort-order characters.
  $orderChars = ""
  $orderHash.Keys | foreach-object { $orderChars += $_ }

  # Keep track of whether '-' appears before a sort-order character.
  $ascendingSort = $TRUE

  # Iterate the sort-order string as a character array.
  foreach ($orderChar in [Char\[]] $orderString) {
    switch -wildcard ($orderChar) {
      "-" {
        if ($ascendingSort) {
          $ascendingSort = $FALSE
        }
      }
      "[$orderChars]" {
        # Output a hashtable containing the requested sort order.
        @{"Expression" = $orderHash\["$_"];
          "Ascending"  = $ascendingSort}
        $ascendingSort = $TRUE
      }
      "G" {
        # Group directories: Sort by the Directory attribute.
        @{"Expression" = {($_.Attributes -band
                          [System.IO.FileAttributes]::Directory) -ne 0};
          "Ascending"  = -not $ascendingSort}
        $ascendingSort = $TRUE
      }
      default {
        throw "Invalid sort-order character ('$_'). Use -help for help."
      }
    }
  }
}
# End Callout D

# Begin Callout E
# Returns the provider name for the specified path. If the path doesn't exist,
# the function returns a blank string.
function get-providername($path) {
  $result = ""
  $pathArg = iif { $LiteralPath } { "-literalpath" } { "-path" }
  $ErrorActionPreference = "SilentlyContinue"
  if (invoke-expression "test-path $pathArg `$path") {
    $result = (invoke-expression ("get-item $pathArg `$path -force |" +
      " select-object -f 1")).PSProvider.Name
  }
  $result
}
# End Callout E


function main {
  # Display the usage message if -help exists.
  if ($Help) {
    usage
  }

  # If -path is missing, assume the current location.
  if ($Path -eq $NULL) {
    $Path = (get-location).Path
  }

  # Use -literalpath if requested; otherwise, just use -path.
  $pathArg = iif { $LiteralPath } { "-literalpath" } { "-path" }

  # If -attributes exists, retrieve the bitmap values.
  if ($Attributes -ne $NULL) {
    $attrInclude,$attrExclude = get-attributeflags $Attributes
  }

  # If -timefield exists, make sure it's valid. LastWriteTime is the default.
  if ($TimeField -ne $NULL) {
    switch -wildcard ($TimeField) {
      "A*" { $TimeField = "LastAccessTime" }
      "C*" { $TimeField = "CreationTime" }
      "W*" { $TimeField = "LastWriteTime" }
      default {
        throw "Invalid time field ('$TimeField'). Use -help for help."
      }
    }
  } else {
    $TimeField = "LastWriteTime"
  }

  # Use the FullName property if requested or if using -recurse.
  $nameField = iif { $FullName -or $Recurse } { "FullName" } { "Name" }

  # If -order exists, retrieve the sort order.
  if ($Order -ne $NULL) {
    $Order = get-orderlist $Order $nameField $TimeField
  }

# Begin Callout F
  # Create the pipeline for the get-childitem cmdlet.
  $pipeline = ""

  # Add -recurse if requested.
  if ($Recurse) {
    $pipeline += " -recurse"
  }

  # If -attributes exists, use -force.
  if ($Attributes -ne $NULL) {
    $pipeline += " -force"
    # If any attributes were specified, pipe to a where-object scriptblock.
    if (($attrInclude -ne 0) -or ($attrExclude -ne 0)) {
      $pipeline += " | where-object { "
      if (($attrInclude -ne 0) -and ($attrExclude -ne 0)) {
        $pipeline += "((`$_.Attributes -band $attrInclude) -eq $attrInclude) -and " +
                     "((`$_.Attributes -band $attrExclude) -eq 0)"
      } elseif ($attrInclude -ne 0) {
        $pipeline += "(`$_.Attributes -band $attrInclude) -eq $attrInclude"
      } else {
        $pipeline += "(`$_.Attributes -band $attrExclude) -eq 0"
      }
    $pipeline += " }"
    }
  }

  # Pipe to sort-object if needed.
  if ($Order -ne $NULL) {
    $pipeline += " | sort-object `$Order"
  }
# End Callout F

  # If -defaultoutput exists, execute the expression and return.
  if ($DefaultOutput) {
    invoke-expression "get-childitem $pathArg `$Path $pipeline"
    return
  }

  # Create the formatted string expression.
  $formatStr = "`"{0,5} {1,17}  {2,8} {3,15:N0}"
  $formatStr += iif { -not $Q } { " {4}" } { " {4,-22} {5}" }
  $formatStr += "`" -f `$_.Mode," +
    "`$_.$TimeField.ToString('d')," +
    "`$_.$TimeField.ToString('t')," +
    "`$_.Length"
  if ($Q) {
    $formatStr += ",(get-acl `$_.FullName).Owner"
  }
  $formatStr += ",`$_.$nameField"

  # Initialize the counters.
  $dirCount = $fileCount = $sizeTotal = 0

  # Iterate each path. Paths must be in the file system.
  foreach ($item in $Path) {
    switch (get-providername $item) {
      "FileSystem" {
        invoke-expression "get-childitem $pathArg `$item $pipeline" |
          foreach-object {
          if (-not $Bare) {
            invoke-expression $formatStr
            if (($_.Attributes -band [System.IO.FileAttributes]::Directory) -eq 0) {
              $fileCount += 1
              $sizeTotal += $_.Length
            } else {
              $dirCount += 1
            }
          } else {
            $_.$nameField
          }
        }
      }
      "" {
        write-error "Cannot find path '$item' because it does not exist."
      }
      default {
        write-error "The path '$item' is not in the file system."
      }
    }
  }

  # Output footer information when not using -bare.
  if (-not $Bare) {
    if (($fileCount -gt 0) -or ($dirCount -gt 0)) {
      "{0,16:N0} file(s) {1,16:N0} byte(s)`n{2,16:N0} dir(s)" -f
        $fileCount,$sizeTotal,$dirCount
    }
  }

}

main

Related Content:

ARTICLE TOOLS

Comments
    There are no comments to display. Be the first one!
You must log on before posting a comment.

Are you a new visitor? Register Here

advertisement

advertisement

Windows is a trademark of the Microsoft group of companies. Windows IT Pro is used by Penton Media Inc. under license from owner.