Search notes:

Powershell module: filesystem

Functions

initialize-emptyDirectory $dir Creates an empty directory named $dir and returns the corresponding System.IO.DirectoryInfo object. If $dir already exists, it is removed (including content) and re-created. If the directory cannot be removed, the function returns $null
resolve-relativePath Returns the relative path between a directory and a (single or array of) file(s) or directory(s). (Inspired by Carbon's command Resolve-RelativePath, uses the WinAPI function PathRelativePathTo).
write-file Creates or overwrites a file whose content is given in a string. Necessary paths of the destination directory are created.
test-fileLock Checks if a file can be opened(?). Returns $true if the file is locked, $false if it is available and $null if the file does not exist at all.
get-openFileProcess On Windows: Returns an array of System.Diagnostic.Process that specify the processes that have a given file opened.
set-locationDocuments Changes the current directory to the documents folder.

filesystem.psm1

set-strictMode -version latest

$winApi = add-type -name filesystem -namespace tq84 -passThru -memberDefinition '
  [DllImport("shlwapi.dll", CharSet=CharSet.Auto)]
  public static extern bool PathRelativePathTo(
     [Out] System.Text.StringBuilder pszPath,
     [In ] string pszFrom, [In ] System.IO.FileAttributes  dwAttrFrom,
     [In ] string pszTo  , [In ] System.IO.FileAttributes  dwAttrTo
);
'
function initialize-emptyDirectory {

 #
 # Create directory $directoryName
 # If it already exists, clean it
 #

   param (
      [string] $directoryName
   )

   if (test-path $directoryName) {
      try {
       #
       # Try to remove directory.
       # Use -errorAction stop so that catch block
       # is executed if unsuccessful
       #
         remove-item -recurse -force -errorAction stop $directoryName
      }
      catch {
         return $null
      }
   }

   new-item $directoryName -type directory
}

function resolve-relativePath {
 #
 # Inspired by https://get-carbon.org/Resolve-RelativePath.html
 #
 # resolve-relativepath .\dir\subdir .\dir\another\sub\dir\file.txt
 #
   param (
      [parameter (
          mandatory        = $true
       )][string                        ]  $dir  ,


      [parameter (
          mandatory        = $true
       )][string[]                     ]  $dest
   )

 #
 # The WinAPI function PathRelativePathTo requires directory separators to be backslashes:
 #
   $dir  = $dir  -replace '/', '\'
   $dest = $dest -replace '/', '\'

   $relPath = new-object System.Text.StringBuilder 260

   [string[]] $ret = @()

   foreach ($dest_ in $dest) {
      $ok = [tq84.filesystem]::PathRelativePathTo($relPath, $dir, [System.IO.FileAttributes]::Directory, $dest_, [System.IO.FileAttributes]::Normal)
      $ret += $relPath.ToString()
   }

   return $ret
}

function write-file {
 #
 # write-file C:\users\rny\test\more\test\test\test.txt "one`ntwo`nthree"
 # write-file ./foo/bar/baz/utf8.txt      "B�rlauch"
 # write-file ./foo/bar/baz/win-1252.txt  "B�rlauch`nLibert�, Fraternit�, Kamillentee"  ( [System.Text.Encoding]::GetEncoding(1252) )
 #
   param (
      [parameter (mandatory=$true)]
      [string] $file,

      [parameter (mandatory=$true)]
      [string] $content,

      [parameter (mandatory=$false)]
      [System.Text.Encoding] $enc = [System.Text.UTF8Encoding]::new($false) # UTF8 without BOM
   )

   $abs_path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($file)
   $abs_dir  = $ExecutionContext.SessionState.Path.ParseParent($abs_path, $null)

   if (! (test-path $abs_dir)) {
      $null = mkdir $abs_dir
   }

   if (test-path $abs_path) {
      remove-item $abs_path
   }

   [System.IO.File]::WriteAllText($abs_path, $content, $enc)
}

function test-fileLock {
  #
  # Inspired by
  #
  # http://mspowershell.blogspot.com/2008/07/locked-file-detection.html
  #
  # Attempts to open a file and trap the resulting error if the file is already open/locked

    param (
       [parameter (mandatory=$true)]
       [string]$filePath
    )

    if (! (test-path $filePath) ) {
       return $null
    }

    $filelocked = $false
    $fileInfo = new-object System.IO.FileInfo $filePath

    trap {
        set-variable -name filelocked -value $true -scope 1
      # $fileLocked = $true
        continue
    }

    $fileStream = $fileInfo.Open( [System.IO.FileMode]::OpenOrCreate,[System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None )
    if ($fileStream) {
        $fileStream.Close()
    }

    $fileLocked
}

function get-openFileProcess {
 #
 # Copied and adapted from
 #   https://github.com/pldmgg/misc-powershell/blob/master/MyFunctions/PowerShellCore_Compatible/Get-FileLockProcess.ps1
 #
   [cmdletBinding()]
    param(
        [parameter(mandatory=$true)]
        [string] $filePath
    )


    if (! $(test-path $filePath)) {
        write-error "The path $filePath was not found! Halting!"
        return
    }

    $csSrc = @"

    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using System;
    using System.Diagnostics;

    namespace tq84 {

        static public class rstrtmgr {

           [StructLayout(LayoutKind.Sequential)]

            struct RM_UNIQUE_PROCESS {
                public int dwProcessId;
                public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
            }

            const int RmRebootReasonNone  =   0;
            const int CCH_RM_MAX_APP_NAME = 255;
            const int CCH_RM_MAX_SVC_NAME =  63;

            enum RM_APP_TYPE {
                RmUnknownApp  =    0,
                RmMainWindow  =    1,
                RmOtherWindow =    2,
                RmService     =    3,
                RmExplorer    =    4,
                RmConsole     =    5,
                RmCritical    = 1000
            }

           [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
            struct RM_PROCESS_INFO {
                public RM_UNIQUE_PROCESS Process;

               [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
                public string strAppName;

               [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
                public string strServiceShortName;

                public RM_APP_TYPE ApplicationType;
                public uint AppStatus;
                public uint TSSessionId;
               [MarshalAs(UnmanagedType.Bool)]
                public bool bRestartable;
            }

           [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
            static extern int RmRegisterResources(
               uint                pSessionHandle,
               UInt32              nFiles,
               string[]            rgsFilenames,
               UInt32              nApplications,
          [In] RM_UNIQUE_PROCESS[] rgApplications,
               UInt32              nServices,
               string[]            rgsServiceNames);

           [DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
            static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);

           [DllImport("rstrtmgr.dll")]
            static extern int RmEndSession(uint pSessionHandle);

           [DllImport("rstrtmgr.dll")]
            static extern int RmGetList(
                        uint              dwSessionHandle,
              out       uint              pnProcInfoNeeded,
              ref       uint              pnProcInfo,
             [In, Out]  RM_PROCESS_INFO[] rgAffectedApps,
              ref       uint              lpdwRebootReasons);


          //
          // http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
          // https://github.com/wyday/wyupdate/blob/main/frmFilesInUse.cs
          //
             static public List<Process> GetOpenFileProcess(string path) {
                uint handle;
                string key = Guid.NewGuid().ToString();
                List<Process> processes = new List<Process>();

                int res = RmStartSession(out handle, 0, key);
                if (res != 0) throw new Exception("Could not begin restart session.  Unable to determine file locker.");

                try {
                    const int ERROR_MORE_DATA = 234;
                    uint  pnProcInfoNeeded    =   0,
                          pnProcInfo          =   0,
                          lpdwRebootReasons   = RmRebootReasonNone;

                    string[] resources = new string[] { path }; // Just checking on one resource.

                    res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);

                    if (res != 0) throw new Exception("Could not register resource.");

                    //Note: there's a race condition here -- the first call to RmGetList() returns
                    //      the total number of process. However, when we call RmGetList() again to get
                    //      the actual processes this number may have increased.
                    res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);

                    if (res == ERROR_MORE_DATA) {

                     // Create an array to store the process results
                        RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
                        pnProcInfo = pnProcInfoNeeded;

                     // Get the list
                        res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
                        if (res == 0) {
                            processes = new List<Process>((int)pnProcInfo);

                         // Enumerate all of the results and add them to the
                         // list to be returned
                            for (int i = 0; i < pnProcInfo; i++) {
                                try
                                {
                                    processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
                                }
                             // catch the error -- in case the process is no longer running
                                catch (ArgumentException) { }
                            }
                        }
                        else throw new Exception("Could not list processes locking resource.");
                    }
                    else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
                }
                finally {
                    RmEndSession(handle);
                }

                return processes;
            }
        }
    }
"@


     add-type -typeDef  $csSrc

     return [tq84.rstrtmgr]::GetOpenFileProcess($filePath)

<# TODO: LINUX

    if ($PSVersionTable.Platform -ne $null -and $PSVersionTable.Platform -ne "Win32NT") {
        $lsofOutput = lsof $filePath

        function Parse-lsofStrings ($lsofOutput, $Index) {
            $($lsofOutput[$Index] -split " " | foreach {
                if (![String]::IsNullOrWhiteSpace($_)) {
                    $_
                }
            }).Trim()
        }

        $lsofOutputHeaders = Parse-lsofStrings -lsofOutput $lsofOutput -Index 0
        $lsofOutputValues = Parse-lsofStrings -lsofOutput $lsofOutput -Index 1

        $Result = [pscustomobject]@{}
        for ($i=0; $i -lt $lsofOutputHeaders.Count; $i++) {
            $Result | Add-Member -MemberType NoteProperty -Name $lsofOutputHeaders[$i] -Value $lsofOutputValues[$i]
        }
    }

#>

    $Result

}
function set-locationDocuments() {
   set-location ([System.Environment]::GetFolderPath('MyDocuments'))
}
Github repository ps-modules-filesystem, path: /filesystem.psm1

filesystem.psd1

@{
   RootModule        = 'filesystem.psm1'
   ModuleVersion     = '0.9'
   FunctionsToExport = @(
     'initialize-emptyDirectory',
     'resolve-relativePath',
     'write-file',
     'test-fileLock',
     'get-openFileProcess',
     'set-locationDocuments'
   )
}
Github repository ps-modules-filesystem, path: /filesystem.psd1

History

0.10 add function set-locationDocuments (2021-07-23).

See also

PowerShell module filesystem, example: copy and modify files in a filesystem tree
René's simple PowerShell modules

Index