# Enforcing Paired Statements Using ScriptBlock

I really like c#'s Using statement as it allows you to pair Open statements with their corresponding Close/Dispose statements in an elegant way which matches C#'s syntax. I realised that something similar can be achieved in PowerShell; e.g. Push-Location should be paired with a Pop-Location, and this can be enforced by creating a function which allows you to use a USING-like syntax.

e.g.

function PushDPopD {
[CmdletBinding()]
param (
[string]$Path , [scriptblock]$Code
)
process {
if (Push-Location $Path -PassThru) { #only proceed with this function's logic if our pushd were successful try {$Code.Invoke()
} finally { #ensure that whatever happens in user code, we always popd after our pushd
Pop-Location
}
}
}
}
function ShowCurrentLocation {
("You are in: {0}" -f (Get-Location).Path)
}

Clear-Host
[string]$localVariable = "We keep variables even in scriptblock context" write-host "nn==Healthy Demo==nn" -ForegroundColor cyan ShowCurrentLocation PushDPopD 'c:\Users' { "hello" ShowCurrentLocation$localVariable
"bye"
}
ShowCurrentLocation

write-host "nn==Exception Demo==nn" -ForegroundColor cyan
ShowCurrentLocation
PushDPopD 'c:\ThisPathDoesNotExist' {
"hello"
ShowCurrentLocation
$localVariable "bye" } ShowCurrentLocation  To me this seems like a logical approach / looks neater than manually checking that all pushes are correctly paired with pops. The only potential issue is if someone includes a popd in the scriptblock itself; but that's no worse than the original scenario, so isn't a big drawback. This also gives us the advantage that code won't blindly run on thinking it's in a different directory to the one specified if the pushd fails. As a concept does this seem logical / have I missed anything. NB: I'm aware it's not currently following verb-noun convention; that's because it's just proof of concept code, so I've not yet put any thought into suitable naming (suggestions welcome though). • One issue occurred; if the scriptblock throws an error (e.g. I add a new line with 1/0 somewhere in the Code parameter's value, the error will show as coming from the $Code.Invoke() line, rather than the actual line-in-error. – JohnLBevan Nov 12 '15 at 13:18

To be honest I feel this might be overcomplicating things.

I use Push-Location/Pop-Location in try/finally blocks when needed (like when loading the SQLPS module), but that's almost never.

For the most part I think one should avoid changing the current location and instead reference the desired path (either relative or absolute). So something like:

$p =$BasePath | Join-Path -ChildPath 'subfolder'
Get-ChildItem -Path $p  rather than: $p = $BasePath | Join-Path -ChildPath 'subfolder' Push-Location$p
try {
Get-ChildItem
} finally {
Pop-Location
}


As you've seen, there are also some issues with using scriptblocks in that way. You might have scope issues with variables, and call stack/execution context will be a bit different. This might get complicated further when you consider pipelines.

So in summary I'd rather avoid changing the working directory and then not obfuscate the process when I do so it's clear what's going on.