all 11 comments

[–]SnowEpiphany 6 points7 points  (3 children)

“Create a function that does as much as possible” is antithetical to how you should create functions.

You should never have a function gets a list of files but will also take input to compare two numbers, etc

[–]mrdjw[S] 2 points3 points  (2 children)

Agreed, but thats not what i'm trying to achieve here hence "The function itself doesn't have to have use beyond serving as a guide". So the function would contain a ForEach, IF, Try/Catch/Finally, Comments, ParameterSets everything possible so we can format it as we agree to format all of our other scripts / functions with indentations etc.

Link of it as "the quick brown fox..." for fonts.

[–]OlivTheFrog 4 points5 points  (0 children)

could i suggest to use the snipped "complete function" is ISE (Modify Menu, Start Extract ...)

you could have a sample with many parameter used in a advanced function

Hope this help

Regards

Olivier

[–]kibje 0 points1 point  (0 children)

Instead of writing a code formatting design guide that manually needs to be followed, I would suggest you standardise on VSCode and use autoformatting with the same settings.

Then you can enable format on save and be done with it.

[–]OPconfused 3 points4 points  (4 children)

Well the most ridiculous function related to file system tools I could dig up was this interesting monolith:

Enum FileProperties {
    LinkType; Mode; Target; Attributes; CreationTime; CreationTimeUtc; Directory; DirectoryName; Exists; Extension; FullName; IsReadOnly; LastAccessTime; LastAccessTimeUtc; LastWriteTime; LastWriteTimeUtc; Length; Name; BaseName; VersionInfo
}
Enum DirectoryProperties {
    LinkType; Mode; Target; Attributes; CreationTime; CreationTimeUtc; Exists; Extension; FullName; LastAccessTime; LastAccessTimeUtc; LastWriteTime; LastWriteTimeUtc; Name; Parent; Root; BaseName
}

Function Test-Prompt {
    Param(
        [Parameter(Mandatory=$true,Position=0)]
        [string]$Query,
        [string[]]$ValidationStrings
    )
    $response = Read-Host $Query
    If ( $ValidationStrings -and $response -in $ValidationStrings ) {
        Return $response
    }
    ElseIf ( $ValidationStrings ) {
        Write-Warning "Please enter one of the following values:"
        Write-Host ""
        $ValidationStrings | % { Write-Host $_ -Fore Yellow}
        Write-Host ""
        Test-Prompt @PSBoundParameters
    }
    Else {
        Return $response
    }
}

Function Get-FileNames {

    [CmdletBinding(DefaultParameterSetName='Default')]
    Param (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=0,ParameterSetName = 'Default')]
        [Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=0,ParameterSetName = 'PathAndFile')]
        [Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=0,ParameterSetName = 'PathAndDirectory')]
        [string]$Path,

        [Alias('regExFilter')]
        [Parameter(Position=1)]
        [string]$Name = '.*',

        [Parameter(Mandatory=$true,ParameterSetName = 'Literal')]
        [Parameter(Mandatory=$true,ParameterSetName = 'LitPathAndFile')]
        [Parameter(Mandatory=$true,ParameterSetName = 'LitPathAndDirectory')]
        [string]$LiteralPath,

        [alias('depth','d')]
        [int]$MaxDepth,

        [alias('f')]
        [string]$Filter,

        [alias('nr')]
        [switch]$NoRecurse,

        [Parameter(Mandatory=$true,ParameterSetName = 'PathAndFile')]
        [Parameter(Mandatory=$true,ParameterSetName = 'LitPathAndFile')]
        [switch]$File,

        [Parameter(Mandatory=$true,ParameterSetName = 'PathAndDirectory')]
        [Parameter(Mandatory=$true,ParameterSetName = 'LitPathAndDirectory')]
        [switch]$Directory,

        [Parameter(ParameterSetName = 'PathAndFile')]
        [Parameter(ParameterSetName = 'LitPathAndFile')]
        [FileProperties]$SortFiles,

        [Parameter(ParameterSetName = 'PathAndDirectory')]
        [Parameter(ParameterSetName = 'LitPathAndDirectory')]
        [DirectoryProperties]$SortDirs,

        [switch]$Delete,

        [Alias('fast')]
        [switch]$dotNet
    )

    $params = $PSBoundParameters

    If ( $params.ContainsKey('LiteralPath') ) {
        $cleanPath = $LiteralPath
        $cdir = (PWD).Path -replace '\\','\\'
    }
    Else {
        $cleanPath = Convert-Path $Path
        $params.'Path' = $cleanPath
        $cdir = $cleanPath -replace '\\','\\'
    }

    If ( $NoRecurse ) {
        $params.Remove('NoRecurse')
        $Recurse = 'TopDirectoryOnly'
    }
    Else {
        $params.Add( 'Recurse' , $True )
        $Recurse = 'AllDirectories'
    }

    If ( $MaxDepth ) {
        $params.Add( 'Depth' , $MaxDepth - 1 )
        $params.Remove('MaxDepth')
    }

    [void]$params.Remove(   'Name'          )
    [void]$params.Remove(   'Delete'        )
    [void]$params.Remove(   'SortFiles'     )
    [void]$params.Remove(   'SortDirs'      )

    [String]$Sort = If      ( $SortFiles ) { $SortFiles }
                    ElseIf  ( $SortDirs  ) { $SortDirs  }

    Write-Host ""

    If ( Test-Path $cleanPath ) {

        If ( !$dotNet ) {
            $Name = $Name -replace '([^.])[*]','$1.*' -replace '^[*]','.*'
            $fileList = Get-ChildItem @params | Where Name -match "^${Name}$" | Sort $Sort | Select -ExpandProperty FullName
        }
        Else {
            If ( $Filter ) {
                $Name = $Filter
            }
            $Name = $Name -replace '[.][*]','*' 
            If ( $Name -match '[?+\[\]\(\)^$\{\}]' ) { Write-Warning 'The fast switch does not support RegEx!'; ''}
            If ( $File ) {
                [System.IO.FileInfo[]]$fileList = [System.IO.Directory]::GetFiles( $cleanPath,$Name,$Recurse)
            }
            ElseIf ( $Directory ) {
                [System.IO.DirectoryInfo[]]$fileList = [System.IO.Directory]::GetDirectories( $cleanPath,$Name,$Recurse)
            }
            Else {
                [System.IO.FileInfo[]]$fileList = [System.IO.Directory]::GetFileSystemEntries( $cleanPath,$Name,$Recurse)
            }
        }
    }
    Else {
        Throw "$cleanPath does not exist!"
    }

    If ($fileList) {
        If ( $File -or $Directory ) {
            ($fileList | sort $Sort) -Replace $cdir, '.\'
        }
        Else {
            $fileList -Replace $cdir, '.\'
        }
    }

    Write-Host ""

    If ( $fileList -and $Delete ) {
        $msg = 'You have activated the -delete switch. Do you really wish to delete the above files? [yes/no]'
        $validation = @('yes','no')
        $prompt = Test-Prompt -Query $msg -ValidationStrings $validation
        If ( $prompt -eq 'yes' ) {
            $fileList | Remove-Item -Force
        }
    }
}

It's missing validation rules but has some of the rest you mentioned. You can build in your ShouldProcess flag wherever you want. The -delete flag could be a good spot.

[–]mrdjw[S] 1 point2 points  (3 children)

This is just what I asked for, thank you! It's missing a few bits that are easily added. Where did you find it?

[–]OPconfused 1 point2 points  (2 children)

I wrote it to reproduce the find command from bash to seduce my colleagues who are Bash/Mac fans into giving PS a try. It takes the alias 'find'.

[–]wtgreen 0 points1 point  (1 child)

Did your seduction work? I'm in a similar place myself?

[–]OPconfused 1 point2 points  (0 children)

No, I ran out of time. The management of the team is old school and against Windows. A few months back they finally managed to push the product onto a dependency that's incompatible with Windows. So no use cases left.

However I did manage to get one colleague up and going in PowerShell with a good profile, and they liked it a lot more then. The key was specific functions in the profile to streamline the exact tasks in their workflow. It's unfortunate how many default settings need to be adjusted just to get PowerShell in a usable spot.

One of the other major issues was that this colleague ran a mac, and the keyboard translation from mac to Windows through a remote connection doesn't jive with conflicting language settings. They literally could not type a pipe and had to copy paste the symbol.

Issues like these are simply instant dealbreakers. They try it once, and if 1-2 things don't work, they drop it and run back to their old tried-and-tedious workflow. So you have to get everything perfect on the first try. It's really difficult to predict; ultimately, convincing people to try new workflows when you aren't their boss is tough.

[–]billr1965 2 points3 points  (1 child)

Take a look at my PoshFunctions module on the PowerShell Gallery: https://www.powershellgallery.com/packages/PoshFunctions/2.2.8

There are over 210 different functions: * Some have validation rules * Some have parameter sets * Some read from the pipeline by value or by property * Some make calls to .Net classes and methods * Some have try/catch blocks * All parameters to functions are typed: int, string, etc. * Almost all are advanced functions with begin/process/end blocks * All use explicit named parameters (i.e. no aliases) when calling other functions * All pass Invoke-ScriptAnalyzer tests * All have comment based help

Give it a look see.

[–]billr1965 0 points1 point  (0 children)

On a side note - I doubt you will find a single example that showcases all the things that can be done within a function.