PowerShellPracticeAndStyle
PowerShellPracticeAndStyle copied to clipboard
New Practices for PowerShell Core
There are going to be a few new things which we need to keep in mind to make our scripts work across multiple platforms. Windows, Nano, Linux, FreeBSD and OSX, ARM and IoT ...
Best Practices for Scripts and Modules:
- Don't put aliases into scripts. Aliases are (currently) different on each platform. Double-check using a tool like ResolveAlias.
- Add the shebang to scripts:
#!/usr/bin/env pwsh(or the more fragile:#!/usr/bin/pwsh -noprofile) - Save scripts with Unix style line endings, or the shebang will not work.
Mac and Windows both accept\nbut the Unix shell interpreter will choke on the carriage return in the shebang. If you don't add the shebang, this doesn't matter. Note that you can always fix it:Set-Content $scriptPath ((Get-Content $scriptPath -raw) -replace "\r") -Encoding utf8 - Always encode in utf-8.
- Be careful with paths. Use forward slashes. Use
Join-PathandSplit-Path - ONLY use the new
-PSEditionvalue allowed for#requiresor module manifests, when you need to restrict to Core, not for Desktop, since it only works in PowerShell 5.1+ - ALWAYS use three digits for versions (i.e. 5.0.0, not 5.0) because they may be parsed as SemanticVersion which currently doesn't work unless you provide the patch version explicitly.
[System.Environment]::CurrentDirectorydoesn't in .Net Core. But if you need to call path-sensitive .Net APIs, you need to use[System.IO.Directory]::SetCurrentDirectory( (Get-Location -PSProvider FileSystem).ProviderPath )to update the environment. Note that PowerShell sets the working directory fine when launching apps...
Finally: Test the code on Linux and Nano, if possible. There are differences.
ProTips
Use this in your profile: $PSDefaultParameterValues["Out-File:Encoding"] = "UTF8" to help with 1.
Don't forget you can install PowerShell 6 alphas side-by-side on Windows (they install to Program Files, and don't use the same profile or module paths). You don't have to set up docker or a VM to get started. It's just that in that scenario, you have access to things you won't have access to on Unix (like Set-ExecutionPolicy), so you should test elsewhere before publishing.
Cmdlet Differences
- Invoke-WebRequest uses HttpClient instead of WebClient, has totally different exceptions
Always encode in utf-8
BOM or no BOM? That's the question. :-) IIRC the .NET UTF8 encoder has BOM enabled by default and PS uses it that way.
Add the shebang to scripts: #!/usr/bin/powershell (or for mac: #!/usr/local/bin/powershell ... ??)
Surely Bash scripts must already have a way to handle the platform difference. Maybe you specify both and multiple #! are searched until the shell is found?
Instead of specifying \n, I would do something like:
$NL = [System.Environment]::NewLine
It would be nice if this were added as an automatic variable.
ALWAYS use three digits for versions
:+1:
OK, changed it to the best thing, which is #!/usr/bin/env powershell ... which would use the first powershell on the path.
But FreeBSD is a pain (and thus, OS X) and /usr/local/bin is not necessarily on the path.
Also, you can't specify parameters to PowerShell if you do that, like: #!/usr/bin/powershell -noprofile works, but #!/usr/bin/env powershell -noprofile doesn't, because the interpreters will try to look up "powershell -noprofile" so a ProTip is going to be to figure out a way to tell (in your profile) if PowerShell is being used as an interpreter vs. as a shell.
I will reword the bit about \n -- the point is that you must save your actual PowerShell script with JUST newlines, or shebangs do not work.
That's a pain in the ass. Git will convert line endings for you, but not sure what happens if we're publishing to the Gallery from a Windows machine.
Just ... stop using carriage returns. It's 2016. 🎱 says YAGNI
It's not like we're manually typing \r\n after every line. The editors do it. :P
Other than notepad.exe, it's always a setting.
Oh? Where's that setting in the ISE?
Are you sure Visual Studio allows you to configure line endings? You can configure it to warn you of inconsistent line endings.
OMG. I just remembered the other reason I despise ISE.
@Jaykul You only have two reasons for that? 😛
Don't get him started! :P
I will reword the bit about \n -- the point is that you must save your actual PowerShell script with JUST newlines, or shebangs do not work.
I think it is worth differentiating this point between how you save your scripts and what you use in your scripts for NL.
I guess if you're using PowerShell on other OS's, you're going to outgrow ISE and start using VS Code anyway.
Also, if don't need the shebang functionality (i.e. running PowerShell scripts "as apps" or from bash), don't worry about it -- they work fine in PowerShell regardless.
@rkeithhill I had simply forgotten that particular reason. Anyway. Visual Studio just uses what's in the file already for encoding and for line endings, you just have to hit the File -> Advanced Save Options to set it, and it figures it out from the file from then on...

There's quite a difference between authoring scripts that are compatible with other platforms and using them on Linux / Mac myself. I don't need to "outgrow" my preferred editor, thank you very much.
I suspect that either David or Tobias will add that option now that we've realized it's a problem.
Or, you know ... perl -i -pe's/\r$//;' .\yourscript.ps1
Or powershell '&{param($f)sc $f ((gc $f -raw)-replace''\r'') -Enc utf8}' .\yourscript.ps1
If you set the encoding at the same time, you could use a file watcher and .... sigh
I may move to VSCode for xplat use but for me ISE currently does exactly what I want it to. Though I am with @dlwyatt and hope that @daviwil will get chance to add this to ISE and preferably the ISE-Preview as that should be much easier to ship updates to.
In case anyone is reading this and needs to know - the line endings setting in VSCode is \r\n by default. Look for the settings files.eol and change it to \n. Found this today after I discovered my .gitignore file wasn't working because of Windows-style line endings.
@kilasuit I agree with you about ISE. I'm so used to the debugging experience there that it will be hard to switch fully to VSCode, although I now prefer it as my go-to general purpose editor. For me the debugging is just a bit slow and clunky-feeling for PowerShell. I'm hoping that changes as VSCode matures - maybe we'll get an all-in-one terminal and debugger? Or maybe I'll just get used to the one in VSCode and won't have to worry about multiple editors anymore? I think only @Daviwil knows the answer to this right now ;-P
@mattmcnabb Keep in mind that with the PowerShellEditorServices, where the debugger lives, we're only at version 0.7.0. It will get better, then worse for a bit, then better. :-) Hopefully by 1.0.0 it will meet most folks expectations.
Also, last I checked, setting function, conditional and action breakpoints is not something ISE natively supports in the UI nor is there debug UI for inspecting variables and bouncing around the call stack. Hopefully, we'll soon have the ability to set variable values from the debug UI in VSCode.
@rkeithhill I fully understand where you're at with the PowerShell extension, and don't get me wrong - I really dig it. I've been using VSCode for a while now to to markdown and some other general editing, so doing PowerShell in it is very attractive. Cross-platform makes this even more attractive. Although I'm more used to the ISE, it also hasn't really developed much since v3.0 so I'm really looking forward to how VSCode and the PowerShell extension progress.
You're right about the ISE not supporting the more advanced debugging like conditional breakpoints - I still use Set-PSBreakpoint for those scenarios. The immediacy that I get with the ISE is due to the debugging session taking place in the active console. It feels very natural to me since this is where I learned debugging in the first place. I've also been using ISESteroids for a while now, and this has made the ISE a pretty powerfull editor. There's a place in my heart for the ISE, but I believe that VSCode is probably the editor of the future.
EDIT: Kudos to you and @Daviwil for all your great work with the editor stuff in the last year or so, and for your constant community engagement!
@mattmcnabb I'd definitely like to hear more about what's clunky in the debugging experience in VS Code. If you get a chance, wanna start a discussion over at the vscode-powershell repo so we can talk about how to make it better?
Regarding the console experience in VS Code, I definitely want to start leveraging the new Integrated Terminal for the editing experience. Now that the PS on Linux launch is over I'll have some time to devote to figuring this out. I'm also pretty well connected now with the guy who works on that feature in VS Code so hopefully he and I can work together to get the ideal experience together.
Regarding the initial purpose of this issue, I agree completely with the ideas and intent of Jaykul's new best practices. Ship it!
I know we have general Powershell best practices and they are there for a reason, but we should put renewed emphasis on some of them.
- Don't use aliases in scripts/modules
- Use
Join-PathandSplit-Pathwhen manipulating file and directory paths.
+1 on Join-Path and Split-Path. I think using / on all platforms should work fine, but those might help for dealing with path quoting, etc.
Maybe we should just create some default Pester tests that users could run on their code to verify compliance with the new "stylesheet"?
@torgro I think PSScriptAnalyzer rules would be a better fit for this. Depending on your editor you can get live feedback on best practices while you're writing scripts.
@daviwil I'd be glad to open the discussion on this, although I've done little real debugging in VSCode. I tend to do this in the ISE and am ready to accept that the issues are probably my own. I'll dig in and try to work up something a bit more coherent and post it as an issue if it feels necessary. Maybe that will help inform your direction on further integration with the live terminal?
@mattmcnabb I forgot about PSScriptAnalyzer. I think both would be sweet. Reason for that is many people use a regular texteditor like notepad++/notepad/etc. At least that is my impression from forums. Very, very few people use PSSA and/or Pester.
I do most of my work in VSCode (thank you @daviwil if I have not said that before) and use Pester quite a bit. In any project that target cross-platform compatibility, it makes sense to have tests that verify these best practices.
@mattmcnabb yeah, if you can give me an overview of your typical debugging workflow in the ISE that'd help me figure out how to replicate it in VS Code with the integrated terminal. Probably how you use the ISE isn't far off from what others do, so it'd be good to have the general debugging workflow patterns written down somewhere.
And thanks Tore! :)
What would be the best location for storing user configurations (like API clientid and secret) for PowerShell modules? For Windows I used c:\users\stefstr etc.a and user environment variables. For usage on Linux that's not going to work. Any suggestions?
Perhaps just in:
Split-Path $Profile