Get-ChooseFileName.psm1
#initial Get-ChooseFolder script. #this will steal a LOT from Get-ChooseFile function Get-ChooseFileName { <# .SYNOPSIS This script is a bridge between PowerShell and the AppleScript Choose File Name UI primitive. It allows the use of the standard macOS Choose File Name dialog inside a PowerShell script and returns a string for the Posix Path of the new file name you pick. Yes the syntax is very similar to Get-ChooseFolder. If you look at the AppleScript commands I'm using for this module and Get-ChooseFolder, you'll see the same similarity. .DESCRIPTION This module takes advantage of piping commands to /usr/bin/osascript to allow powershell to use AppleScript's Choose File function, (https://developer.apple.com/library/archive/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/PromptforaFileName.html#//apple_ref/doc/uid/TP40016239-CH82-SW1 for more details) As with some of the other modules in this series, this attempts to plug a hole in PowerShell on macOS by allowing access to things that are useful in a GUI, like user input, or choosing a folder/folders. This module takes advantage of osascript's ability to run AppleScript from the Unix shell environment. There are a number of parameters you can use with this, (in -Detailed) to customize the dialog. There is no required parameter, so just running Get-ChooseFileName will give you a basic Choose File Name dialog. Use Get-Help Get-ChooseFileName - Detailed for Parameter List "Normally", there's one error that is thrown by design: if you hit "Cancel" in the choose file dialog, the script will return userCancelError. It's not returned as an *error* but as a string because it's not an error per se. The user hitting cancel is a viable correct option, so returning userCancelError allows you to manage that better. Note that PowerShell is case insensitive, so the parameters are as well .INPUTS None, there's nothing you can pipe to this .OUTPUTS A string representing a POSIX path or a string reading userCancelError .EXAMPLE Basic Choose Folder: Get-ChooseFileName That will give you a dialog that lets you choose a single folder .EXAMPLE Choose File Name with custom prompt: Get-ChooseFileName -chooseFileNamePrompt "My Custom Prompt" .EXAMPLE Choose file name starting in a specified folder: Get-ChooseFileName -defaultLocation "Some unix path" Note that with the default location parameter, you shouldn't have to escape spaces, single quotes etc. Since this is expecting double quotes around the string, if you use a double quote in the file path, you'd have to escape it. HOWEVER, this is WHERE IT GETS WEIRD, because you have to combine unix AND PowerShell escaping. For Example, say the path you want to pass is: /Users/username/Pictures/Bill"s amazing pictures - to get that to work, you'd have to enter: "/Users/username/Pictures/Bill\`"s amazing pictures" because that will allow PowerShell to escape the double quote and pass the string: "/Users/username/Pictures/Bill\"s amazing pictures" to the unix command Try to avoid this, but if you can't, then the order is "PowerShell escape the string so Powershell passes a Unix-escaped string to the Unix command". If it makes your head hurt, JOIN THE CLUB ALSO IMPORTANT: avoid ~. It doesn't work. There's probably some escape magic that makes it work, but I'm too lazy to try to find it. .EXAMPLE Choose file name with a default file name: Get-ChooseFileName -defaultFileName "some file name" This is something you'd probably only use in a script you're building for someone else to run. Like you still have to type the filename anyway. But it's an option in the command, so it's in here too .LINK https://github.com/johncwelch/Get-PSChooseFileName #> Param ( #we do the params this way so the help shows the description [Parameter(Mandatory = $false)][string] #optional, default is nothing, a prompt for the dialog $chooseFileNamePrompt, [Parameter(Mandatory = $false)][string] #the dictionary says this has to be an alias, setting it to POSIX file works too. Enter this as a unix file path. $defaultLocation, [Parameter(Mandatory = $false)][string] #default filename for the dialog $defaultFileName ) if (-Not $IsMacOS) { Write-Output "This module only runs on macOS, exiting" Exit } $chooseFileNameCommand = "choose file name " #prompt processing if(-not [string]::IsNullOrEmpty($chooseFileNamePrompt)) { $chooseFileNameCommand = $chooseFileNameCommand + "with prompt `"$chooseFileNamePrompt`" " } #default locatin processing if(-not [string]::IsNullOrEmpty($defaultLocation)) { #we have a location, but we have to be clever. Since we can't convert the path string to a POSIX file in a variable #we do the conversion in the command itself. Yes we need the quotes in the command once it's expanded, so we escape them $chooseFileNameCommand = $chooseFileNameCommand + "default location (`"$defaultLocation`" as POSIX file) " } #default file name processing if(-not [string]::IsNullOrEmpty($defaultFileName)) { $chooseFileNameCommand = $chooseFileNameCommand + "default name `"$defaultFileName`" " } ##run the command #since we have to get this path back as a unix filepath, we splice in an posix path statement $chooseFileNameCommand = "POSIX path of ($chooseFileNameCommand)" #now we run the command $chooseFileNameString = $chooseFileNameCommand|/usr/bin/osascript -so #deal with cancel if($chooseFileNameString.Contains("execution error: User canceled. `(-128`)")) { #Write-Output "user hit cancel button" return "userCancelError" } return $chooseFileNameString } #what the module shows the world Export-ModuleMember -Function Get-ChooseFileName # SIG # Begin signature block # MIIMgAYJKoZIhvcNAQcCoIIMcTCCDG0CAQMxDTALBglghkgBZQMEAgEwewYKKwYB # BAGCNwIBBKBtBGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDbBesoJU+paRty # /tbzU9NbexE3i8wuHxWbcnFWy8UPBqCCCaswggQEMIIC7KADAgECAggYeqmowpYh # DDANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUg # SW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAU # BgNVBAMTDUFwcGxlIFJvb3QgQ0EwHhcNMTIwMjAxMjIxMjE1WhcNMjcwMjAxMjIx # MjE1WjB5MS0wKwYDVQQDDCREZXZlbG9wZXIgSUQgQ2VydGlmaWNhdGlvbiBBdXRo # b3JpdHkxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRMw # EQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEB # BQADggEPADCCAQoCggEBAIl2TwZbmkHupSMrAqNf13M/wDWwi4QKPwYkf6eVP+tP # DpOvtA7QyD7lbRizH+iJR7/XCQjk/1aYKRXnlJ25NaMKzbTA4eJg9MrsKXhFaWlg # a1+KkvyeI+Y6wiKzMU8cuvK2NFlC7rCpAgMYkQS2s3guMx+ARQ1Fb7sOWlt/OufY # CNcLDjJt+4Y25GyrxBGKcIQmqp9E0fG4xnuUF5tI9wtYFrojxZ8VOX7KXcMyXw/g # Un9A6r6sCGSVW8kanOWAyh9qRBxsPsSwJh8d7HuvXqBqPUepWBIxPyB2KG0dHLDC # ThFpJovL1tARgslOD/FWdNDZCEtmeKKrrKfi0kyHWckCAwEAAaOBpjCBozAdBgNV # HQ4EFgQUVxftos/cfJihEOD8voctLPLjF1QwDwYDVR0TAQH/BAUwAwEB/zAfBgNV # HSMEGDAWgBQr0GlHlHYJ/vRrjS5ApvdHTX8IXjAuBgNVHR8EJzAlMCOgIaAfhh1o # dHRwOi8vY3JsLmFwcGxlLmNvbS9yb290LmNybDAOBgNVHQ8BAf8EBAMCAYYwEAYK # KoZIhvdjZAYCBgQCBQAwDQYJKoZIhvcNAQELBQADggEBAEI5dGuh3MakjzcqjLMd # CkS8lSx/vFm4rGH7B5CSMrnUvzvBUDlqRHSi7FsfcOWq3UtsHCNxLV/RxZO+7puK # cGWCnRbjGhAXiS2ozf0MeFhJDCh/M+4Aehu0dqy2tbtP36gbncgZl0oLVmcvwj62 # s8SDOvB3bXTELiNR7pqlA29g9KVIpwbCu1riHx9GRX7kl/UnELcgInJvctrGUHXF # PSWPXaMA6Z82jEg5j7M76pCALpWaYPR4zvQOClM+ovpP2B6uhJWNMrxWTYnpeBjg # rJpCunpGG4Siic4U6IjRWIv2rlbELAUqRa8L2UupAg80rIjHYVWJRMkncwfuguVO # 9XAwggWfMIIEh6ADAgECAggGHmabX9eOKjANBgkqhkiG9w0BAQsFADB5MS0wKwYD # VQQDDCREZXZlbG9wZXIgSUQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxJjAkBgNV # BAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRMwEQYDVQQKDApBcHBs # ZSBJbmMuMQswCQYDVQQGEwJVUzAeFw0yMDA5MTYwMzU4MzBaFw0yNTA5MTcwMzU4 # MzBaMIGNMRowGAYKCZImiZPyLGQBAQwKNzk2NDg4Vkc5NTE4MDYGA1UEAwwvRGV2 # ZWxvcGVyIElEIEluc3RhbGxlcjogSm9obiBXZWxjaCAoNzk2NDg4Vkc5NSkxEzAR # BgNVBAsMCjc5NjQ4OFZHOTUxEzARBgNVBAoMCkpvaG4gV2VsY2gxCzAJBgNVBAYT # AlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw0uP+x8FCIpcy4DJ # xqWRX3Pdtr55nnka0f22c7Ko+IAC//91iQxQLuz8fqbe4b3pEyemzfDB0GSVyhnY # AYLVYMjVaUamr2j7apX8M3QxIcxrlHAJte1Mo+ntsQic4+syz5HZm87ew4R/52T3 # zzvtsjaKRIfy0VT35E9T4zVhpq3vdJkUCuQrHrXljxXhOEzJrJ9XllDDJ2QmYZc0 # K29YE9pVPFiZxkbf5xmtx1CZhiUulCI0ypnj7dGxLJxRtJhsFChzeSflkOBtn9H/ # RVuBjb0DaRib/mEK7FCbYgEbcIL5QcO3pUlIyghXaQoZsNaViszg7Xzfdh16efby # y+JLaQIDAQABo4ICFDCCAhAwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBRXF+2i # z9x8mKEQ4Py+hy0s8uMXVDBABggrBgEFBQcBAQQ0MDIwMAYIKwYBBQUHMAGGJGh0 # dHA6Ly9vY3NwLmFwcGxlLmNvbS9vY3NwMDMtZGV2aWQwNzCCAR0GA1UdIASCARQw # ggEQMIIBDAYJKoZIhvdjZAUBMIH+MIHDBggrBgEFBQcCAjCBtgyBs1JlbGlhbmNl # IG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0 # YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBj # b25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZp # Y2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMDYGCCsGAQUFBwIBFipodHRwOi8v # d3d3LmFwcGxlLmNvbS9jZXJ0aWZpY2F0ZWF1dGhvcml0eS8wFwYDVR0lAQH/BA0w # CwYJKoZIhvdjZAQNMB0GA1UdDgQWBBRdVgk/6FL+2RJDsLeMey31Hn+TBzAOBgNV # HQ8BAf8EBAMCB4AwHwYKKoZIhvdjZAYBIQQRDA8yMDE5MDIwNjAwMDAwMFowEwYK # KoZIhvdjZAYBDgEB/wQCBQAwDQYJKoZIhvcNAQELBQADggEBAHdfmGHh7XOchb/f # reKxq4raNtrvb7DXJaubBNSwCjI9GhmoAJIQvqtAHSSt4CHsffoekPkWRWaJKgbk # +UTCZLMy712KfWtRcaSNNzOp+5euXkEsrCurBm/Piua+ezeQWt6RzGNM86bOa34W # 4r6jdYm8ta9ql4So07Z4kz3y5QN7fI20B8kG5JFPeN88pZFLUejGwUpshXFO+gbk # GrojkwbpFuRAsiEZ1ngeqtObaO8BRKHahciFNpuTXk1I0o0XBZ2JmCUWzx3a6T4u # fME1heNtNLRptGYMtZXH4tboV39Wf5lgHc4KR85Mbw52srsRU22NE8JWAvgFp/Qz # qX5rmVIxggIrMIICJwIBATCBhTB5MS0wKwYDVQQDDCREZXZlbG9wZXIgSUQgQ2Vy # dGlmaWNhdGlvbiBBdXRob3JpdHkxJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRp # b24gQXV0aG9yaXR5MRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUwII # Bh5mm1/XjiowCwYJYIZIAWUDBAIBoHwwEAYKKwYBBAGCNwIBDDECMAAwGQYJKoZI # hvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcC # ARUwLwYJKoZIhvcNAQkEMSIEIBKvLHxxC9qtbV3R4M17dqsbawU3G2ybalWa5r/B # cMlqMAsGCSqGSIb3DQEBAQSCAQA5wKNOOWWzxEzW4Qw/PB1Y926edPpHImHA2fPd # vFoHjolR98KvBKp/MJPOFi5ss4FDfSohU4zl+VV4JGs3QZsAIyhrPVh8vG/YPeKH # e4qldW++jATq4ZgAPi9Z0oL4XC06JVj9Gqd/9Zm28R1jEZli8QL+oEl/HLyiHzcd # ATyJ1izS/XCvR5tM7ZTA9b06QXj32xSibTd5zPhwjgqKY4sMBriw2iBCzNlUjPwT # JSyWX2G8HyLYm1TzOZUZmPiqvf4ScejzO99Alr2stWA6wezr8LOWacuhrO/0XVbM # gIeX2nWOQCJjp68QTr77LZDNddFrQc5ZezJN5V1YPuBIIqic # SIG # End signature block |